diff --git a/core-scala/src/main/scala/com/baeldung/scala/PatternMatching.scala b/core-scala/src/main/scala/com/baeldung/scala/PatternMatching.scala new file mode 100644 index 0000000000..71458237d6 --- /dev/null +++ b/core-scala/src/main/scala/com/baeldung/scala/PatternMatching.scala @@ -0,0 +1,137 @@ +package com.baeldung.scala + +// Case Class +abstract class Animal + +case class Mammal(name: String, fromSea: Boolean) extends Animal + +case class Bird(name: String) extends Animal + +case class Fish(name: String) extends Animal + +// Sealed Class +sealed abstract class CardSuit + +case class Spike() extends CardSuit + +case class Diamond() extends CardSuit + +case class Heart() extends CardSuit + +case class Club() extends CardSuit + +object Person { + def apply(fullName: String) = fullName + + def unapply(fullName: String): Option[String] = { + if (!fullName.isEmpty) + Some(fullName.replaceAll("(?<=\\w)(\\w+)", ".")) + else + None + } +} + +class PatternMatching { + + def caseClassesPatternMatching(animal: Animal): String = { + animal match { + case Mammal(name, fromSea) => s"I'm a $name, a kind of mammal. Am I from the sea? $fromSea" + case Bird(name) => s"I'm a $name, a kind of bird" + case _ => "I'm an unknown animal" + } + } + + def constantsPatternMatching(constant: Any): String = { + constant match { + case 0 => "I'm equal to zero" + case 4.5d => "I'm a double" + case false => "I'm the contrary of true" + case _ => s"I'm unknown and equal to $constant" + } + } + + def sequencesPatternMatching(sequence: Any): String = { + sequence match { + case List(singleElement) => s"I'm a list with one element: $singleElement" + case List(_, _*) => s"I'm a list with one or multiple elements: $sequence" + case Vector(1, 2, _*) => s"I'm a vector: $sequence" + case _ => s"I'm an unrecognized sequence. My value: $sequence" + } + } + + def tuplesPatternMatching(tuple: Any): String = { + tuple match { + case (first, second) => s"I'm a tuple with two elements: $first & $second" + case (first, second, third) => s"I'm a tuple with three elements: $first & $second & $third" + case _ => s"Unrecognized pattern. My value: $tuple" + } + } + + def typedPatternMatching(any: Any): String = { + any match { + case string: String => s"I'm a string. My value: $string" + case integer: Int => s"I'm an integer. My value: $integer" + case _ => s"I'm from an unknown type. My value: $any" + } + } + + def regexPatterns(toMatch: String): String = { + val numeric = """([0-9]+)""".r + val alphabetic = """([a-zA-Z]+)""".r + val alphanumeric = """([a-zA-Z0-9]+)""".r + + toMatch match { + case numeric(value) => s"I'm a numeric with value $value" + case alphabetic(value) => s"I'm an alphabetic with value $value" + case alphanumeric(value) => s"I'm an alphanumeric with value $value" + case _ => s"I contain other characters than alphanumerics. My value $toMatch" + } + } + + def optionsPatternMatching(option: Option[String]): String = { + option match { + case Some(value) => s"I'm not an empty option. Value $value" + case None => "I'm an empty option" + } + } + + def patternGuards(toMatch: Any, maxLength: Int): String = { + toMatch match { + case list: List[Any] if (list.size <= maxLength) => "List is of acceptable size" + case list: List[Any] => "List has not an acceptable size" + case string: String if (string.length <= maxLength) => "String is of acceptable size" + case string: String => "String has not an acceptable size" + case _ => "Input is neither a List or a String" + } + } + + def sealedClass(cardSuit: CardSuit): String = { + cardSuit match { + case Spike() => "Card is spike" + case Club() => "Card is club" + case Heart() => "Card is heart" + case Diamond() => "Card is diamond" + } + } + + def extractors(person: Any): String = { + person match { + case Person(initials) => s"My initials are $initials" + case _ => "Could not extract initials" + } + } + + def closuresPatternMatching(list: List[Any]): List[Any] = { + list.collect { case i: Int if (i < 10) => i } + } + + def catchBlocksPatternMatching(exception: Exception): String = { + try { + throw exception + } catch { + case ex: IllegalArgumentException => "It's an IllegalArgumentException" + case ex: RuntimeException => "It's a RuntimeException" + case _ => "It's an unknown kind of exception" + } + } +} \ No newline at end of file diff --git a/core-scala/src/test/scala/com/baeldung/scala/PatternMatchingUnitTest.scala b/core-scala/src/test/scala/com/baeldung/scala/PatternMatchingUnitTest.scala new file mode 100644 index 0000000000..21a2f0e871 --- /dev/null +++ b/core-scala/src/test/scala/com/baeldung/scala/PatternMatchingUnitTest.scala @@ -0,0 +1,208 @@ +package com.baeldung.scala + +import java.io.FileNotFoundException + +import org.junit.Assert.assertEquals +import org.junit.Test + +class PatternMatchingUnitTest { + @Test + def whenAMammalIsGivenToTheMatchExpression_ThenItsRecognizedAsMammal(): Unit = { + val result = new PatternMatching().caseClassesPatternMatching(Mammal("Lion", fromSea = false)) + assertEquals("I'm a Lion, a kind of mammal. Am I from the sea? false", result) + } + + @Test + def whenABirdIsGivenToTheMatchExpression_ThenItsRecognizedAsBird(): Unit = { + val result = new PatternMatching().caseClassesPatternMatching(Bird("Pigeon")) + assertEquals("I'm a Pigeon, a kind of bird", result) + } + + @Test + def whenAnUnkownAnimalIsGivenToTheMatchExpression_TheDefaultClauseIsUsed(): Unit = { + val result = new PatternMatching().caseClassesPatternMatching(Fish("Tuna")) + assertEquals("I'm an unknown animal", result) + } + + @Test + def whenTheConstantZeroIsPassed_ThenItMatchesTheCorrespondingPattern(): Unit = { + val result = new PatternMatching().constantsPatternMatching(0) + assertEquals("I'm equal to zero", result) + } + + @Test + def whenFourAndAHalfIsPassed_ThenItMatchesTheCorrespondingPattern(): Unit = { + val result = new PatternMatching().constantsPatternMatching(4.5d) + assertEquals("I'm a double", result) + } + + @Test + def whenTheBooleanFalseIsPassed_ThenItMatchesTheCorrespondingPattern(): Unit = { + val result = new PatternMatching().constantsPatternMatching(false) + assertEquals("I'm the contrary of true", result) + } + + @Test + def whenAnUnkownConstantIsPassed_ThenTheDefaultPatternIsUsed(): Unit = { + val result = new PatternMatching().constantsPatternMatching(true) + assertEquals("I'm unknown and equal to true", result) + } + + @Test + def whenASingleElementListIsPassed_ThenItMatchesTheCorrespondingPattern(): Unit = { + val result = new PatternMatching().sequencesPatternMatching(List("String")) + assertEquals("I'm a list with one element: String", result) + } + + @Test + def whenAMultipleElementsListIsPassed_ThenItMatchesTheCorrespondingPattern(): Unit = { + val result = new PatternMatching().sequencesPatternMatching(List("Multiple", "Elements")) + assertEquals("I'm a list with one or multiple elements: List(Multiple, Elements)", result) + } + + @Test + def whenAVectorBeginningWithOneAndTwoIsPassed_ThenItMatchesTheCorrespondingPattern(): Unit = { + val result = new PatternMatching().sequencesPatternMatching(Vector(1, 2, 3)) + assertEquals("I'm a vector: Vector(1, 2, 3)", result) + } + + @Test + def whenANotMatchingVectorIsPassed_ThenItShouldntMatchAndEnterTheDefaultClause(): Unit = { + val result = new PatternMatching().sequencesPatternMatching(Vector(2, 1)) + assertEquals("I'm an unrecognized sequence. My value: Vector(2, 1)", result) + } + + @Test + def whenAnEmptyListIsPassed_ThenItShouldntMatchAndEnterTheDefaultClause(): Unit = { + val result = new PatternMatching().sequencesPatternMatching(List()) + assertEquals("I'm an unrecognized sequence. My value: List()", result) + } + + @Test + def whenATwoElementsTupleIsPassed_ThenItMatchesTheCorrespondingPattern(): Unit = { + val result = new PatternMatching().tuplesPatternMatching(("First", "Second")) + assertEquals("I'm a tuple with two elements: First & Second", result) + } + + @Test + def whenAThreeElementsTupleIsPassed_ThenItMatchesTheCorrespondingPattern(): Unit = { + val result = new PatternMatching().tuplesPatternMatching(("First", "Second", "Third")) + assertEquals("I'm a tuple with three elements: First & Second & Third", result) + } + + @Test + def whenAnoterKindOfTupleIsPassed_ThenItShouldntMatchAndReturnTheDefaultPattern(): Unit = { + val result = new PatternMatching().tuplesPatternMatching(("First")) + assertEquals("Unrecognized pattern. My value: First", result) + } + + @Test + def whenAStringConsistingOfNumericsOnlyIsPassed_ThenItShouldMatchTheNumericRegex(): Unit = { + val result = new PatternMatching().regexPatterns("123") + assertEquals("I'm a numeric with value 123", result) + } + + @Test + def whenAStringConsistignOfAlphabeticsOnlyIsPassed_ThenItShouldMatchTheAlphabeticRegex(): Unit = { + val result = new PatternMatching().regexPatterns("abc") + assertEquals("I'm an alphabetic with value abc", result) + } + + @Test + def whenAStringConsistignOfAlphanumericsOnlyIsPassed_ThenItShouldMatchTheAlphanumericRegex(): Unit = { + val result = new PatternMatching().regexPatterns("abc123") + assertEquals("I'm an alphanumeric with value abc123", result) + } + + @Test + def whenAnotherTypeOfStringIsPassed_ThenItShouldntMatchAndReturnTheDefaultPattern(): Unit = { + val result = new PatternMatching().regexPatterns("abc_123") + assertEquals("I contain other characters than alphanumerics. My value abc_123", result) + } + + @Test + def whenAFilledOptionIsPassed_ThenItShouldMatchTheSomeClause(): Unit = { + val result = new PatternMatching().optionsPatternMatching(Option.apply("something")) + assertEquals("I'm not an empty option. Value something", result) + } + + @Test + def whenAnEmptyOptionIsPassed_ThenItShouldMatchTheNoneClause(): Unit = { + val result = new PatternMatching().optionsPatternMatching(Option.empty) + assertEquals("I'm an empty option", result) + } + + @Test + def whenAListWithAcceptedSizeIsPassed_ThenThePositiveMessageIsSent(): Unit = { + val result = new PatternMatching().patternGuards(List(1, 2), 3) + assertEquals("List is of acceptable size", result) + } + + @Test + def whenAListWithAnUnacceptedSizeIsPassed_ThenTheNegativeMessageIsSent(): Unit = { + val result = new PatternMatching().patternGuards(List(1, 2, 3, 4), 3) + assertEquals("List has not an acceptable size", result) + } + + @Test + def whenAStringWithAcceptedSizeIsPassed_ThenThePositiveMessageIsSent(): Unit = { + val result = new PatternMatching().patternGuards("OK", 3) + assertEquals("String is of acceptable size", result) + } + + @Test + def whenAStringWithAnUnacceptedSizeIsPassed_ThenTheNegativeMessageIsSent(): Unit = { + val result = new PatternMatching().patternGuards("Not OK", 3) + assertEquals("String has not an acceptable size", result) + } + + @Test + def whenAnObjectWhichIsNotAListOrAStringIsPassed_thenTheDefaultClauseIsUsed(): Unit = { + val result = new PatternMatching().patternGuards(1, 1) + assertEquals("Input is neither a List or a String", result) + } + + @Test + def whenACardSuitIsPassed_ThenTheCorrespondingMatchCaseClauseIsUsed(): Unit = { + assertEquals("Card is spike", new PatternMatching().sealedClass(Spike())) + assertEquals("Card is club", new PatternMatching().sealedClass(Club())) + assertEquals("Card is heart", new PatternMatching().sealedClass(Heart())) + assertEquals("Card is diamond", new PatternMatching().sealedClass(Diamond())) + } + + @Test + def whenAnObjectWithExtractorIsPassed_ThenTheExtractedValueIsUsedInTheCaseClause(): Unit = { + val person = Person("John Smith") + val result = new PatternMatching().extractors(person) + assertEquals("My initials are J. S.", result) + } + + @Test + def whenAnObjectWithExtractorIsPassed_AndTheValueIsEmpty_ThenTheDefaultCaseClauseIsUsed(): Unit = { + val person = Person("") + val result = new PatternMatching().extractors(person) + assertEquals("Could not extract initials", result) + } + + @Test + def whenAListOfRandomElementsIsPassed_ThenOnlyTheIntegersBelowTenAreReturned(): Unit = { + val input = List(1, 2, "5", 11, true) + val result = new PatternMatching().closuresPatternMatching(input) + assertEquals(List(1, 2), result) + } + + @Test + def whenAnExceptionIsPassed_ThenTheCorrespondingMessageIsReturned(): Unit = { + val pm = new PatternMatching() + + val iae = new IllegalArgumentException() + val re = new RuntimeException() + val fnfe = new FileNotFoundException() + + assertEquals("It's an IllegalArgumentException", pm.catchBlocksPatternMatching(iae)) + assertEquals("It's a RuntimeException", pm.catchBlocksPatternMatching(re)) + assertEquals("It's an unknown kind of exception", pm.catchBlocksPatternMatching(fnfe)) + } +} + +