Chapter 15: Scala Exercises With Solutions

By Nadim Bahadoor | Last updated: March 17, 2020 at 9:07 am

This section contains the solutions to the Scala exercises, and problem sets from our scala-exercises section. And, don't forget to get involved, and share any Scala exercises that you think would help the wider Scala community! Instructions for sharing are on the scala-exercises section.

You will further find the source code for all the Scala practice exercises below at our scala-exercises GitHub repository.

Basics

Getting familiar with String and basic text literals

Problem 1: Create a Scala program to reverse, and then format to upper case, the given String: "https://allaboutscala.com/scala-exercises
Output: SESICREXE-ALACS/MOC.ALACSTUOBALLA//:PTTH

Solution:


val strToFormat = "https://allaboutscala.com/scala-exercises"
val reversedAndToUpperCase = strToFormat.reverse.toUpperCase()
println(s"$strToFormat reversed and then to upper case = $reversedAndToUpperCase")

You should see the following output when you run your Scala application in IntelliJ:


https://allaboutscala.com/scala-exercises reversed and then to upper case = SESICREXE-ALACS/MOC.ALACSTUOBALLA//:PTTH

 

Problem 2: Create a Scala program to output the following basic JSON notation.
Output:
{
"donut_name":"Vanilla Donut",
"quantity_purchased":"10",
"price":2.5
}

Solution:


  val donutName = "Vanilla Donut"
  val quantityPurchased = 10
  val price = 2.50
  val donutJson =
    s"""
       |{
       |"donut_name":"$donutName",
       |"quantity_purchased":"$quantityPurchased",
       |"price":$price
       |}
      """.stripMargin
  println(donutJson)

You should see the following output when you run your Scala application in IntelliJ:


{
"donut_name":"Vanilla Donut",
"quantity_purchased":"10",
"price":2.5
}

 

Problem 3: Create a Scala program to prompt customers for their name and age. The format for the name and age labels should be in bold. And, the name literal should be underlined.
Output:
Name: Nadim Bahadoor
Age: 38
Solution:


    import scala.io.StdIn._
    val name = readLine("Enter your name: ")
    println("Enter your age: ")
    val age = readInt()
    println(Console.BOLD)
    print("Name: ")
    print(Console.UNDERLINED)
    print(name)
    println(Console.BOLD)
    print("Age: ")
    print(Console.RESET)
    print(age)

You should see the following output when you run your Scala application in IntelliJ:


Enter your name: Nadim Bahadoor
Enter your age: 
38

Name: Nadim Bahadoor
Age: 38

 

Problem 4: Create a Scala program to find the 8th character in the String: "http://allaboutscala".
Output:
The 8th character literal in https://allaboutscala.com
Solution:


    val str = "https://allaboutscala.com"
    val charToFind = str.charAt(7)
    println(s"The 8th character literal in $str = $charToFind")

You should see the following output when you run your Scala application in IntelliJ:


The 8th character literal in https://allaboutscala.com = a

 

Problem 5: Create a Scala program to calculate the total cost for a customer who is buying 10 Glazed donuts. You can assume that the price of each Glazed donut item is at $2.50. Notice the format of the $25.00 total cost literal, which is essentially at 2 decimal places.
Output:
Total cost of 10 Glazed Donuts = $25.00

Solution:


    val glazedDonut = "Glazed Donut"
    val unitPrice = 2.50
    val qtyPurchased = 10
    val totalCost = qtyPurchased * unitPrice

    println(f"""Total cost of $qtyPurchased $glazedDonut${if (qtyPurchased > 1) "s" else ""} = $$$totalCost%1.2f""")

You should see the following output when you run your Scala application in IntelliJ:


Total cost of 10 Glazed Donuts = $25.00

 

Problem 6: Create a Scala program that will ask the following question to a customer: "What is your favorite movie of all times?".
Output: 

"Customer's favorite movie" is totally awesome!
Solution:


    import scala.io.StdIn._
    val favoriteMovie = readLine("What is your favorite movie of all times?")
    println(s"$favoriteMovie is totally awesome!")

You should see the following output when you run your Scala application in IntelliJ:


What is your favorite movie of all times? The Matrix
 The Matrix is totally awesome!

Problem 7: Create a Scala program to output your name and favorite movie.
Output:
First Name: Nadim
Last Name: Bahadoor
Favorite Movie: The Matrix

Solution:


 val firstName = "Nadim"
 val lastName = "Bahadoor"
 val favoriteMovie = "The Matrix"
 val output =
 s"""
 | First Name: $firstName
 | Last Name: $lastName
 | Favorite Movie: $favoriteMovie
 """.stripMargin

You should see the following output when you run your Scala application in IntelliJ:


 First Name: Nadim
 Last Name: Bahadoor
 Favorite Movie: The Matrix

 

Problem 8: Create a Scala program to parse the corresponding values from the given String "Vanilla Donut 10 2.25", where the literal Vanilla Donut is a particular donut, the 10 literal is the quantity purchased, and 2.25 is the unit price of each Vanilla Donut. You further need to cast each token from the input String to their corresponding types, such as, an Int, Double or String.
Output:
Donut Name: Vanilla Donut
Donut Price: $2.25
Donut Purchased: 10

Solution:


 val donutBoughtStr = "Vanilla Donut 10 2.25"
 val splitStr = donutBoughtStr.split(" ")
 val donutType = splitStr(0)
 val donutName = splitStr(1)
 val donutQty = splitStr(2).toInt
 val donutPrice = splitStr(3).toDouble
 val donutOutput =
  s"""
 |Donut Name: $donutType $donutName
 |Donut Price: $$$donutPrice
 |Donut Purchased: $donutQty
 """.stripMargin
 println(donutOutput)

You should see the following output when you run your Scala application in IntelliJ:


Donut Name: Vanilla Donut
Donut Price: $2.25
Donut Purchased: 10

 

Problem 9: Create a Scala program and use an appropriate data structure to present the following key and value pairs of children and their ages: Bill is 9 years old, Jonny is 8 years old, Tommy is 11 years old, and Cindy is 13 years old. Sort out the corresponding child to age in reverse alphabet order.
Output:
Children to ages in reverse order by their names = Tommy -> 11, Jonny -> 8, Cindy -> 13, Bill -> 9

Solution:


object ReverseAlphaOrder extends Ordering[String]{
 def compare(key1:String, key2:String) = key2.compareTo(key1)
 }

 val childrenToAges = TreeMap(
 ("Bill",9),
 ("Jonny",8),
 ("Tommy",11),
 ("Cindy", 13)
 )(ReverseAlphaOrder)
 println(s"Children to ages in reverse order by their names = ${childrenToAges.mkString(", ")}")

You should see the following output when you run your Scala application in IntelliJ:

Children to ages in reverse order by their names = Tommy -> 11, Jonny -> 8, Cindy -> 13, Bill -> 9

Problem 10: Let us assume that you two shopping baskets with a bunch of items in each one. The first contains elements: "Cake", "Milk", "Cheese", "Toilet Paper", and the second one has the following items: "Bread", "Water", "Juice", "Milk", "Cheese". Write a Scala program to find the common items between the two shopping baskets. You can use whichever data structure that you feel is appropriate for this particular problem.
Output:
Shopping Basket One = Cake - Milk - Cheese - Toilet Paper
Shopping Basket Two = Water - Milk - Juice - Cheese - Bread
Common items are:

MILK
CHEESE

Solution:


 val setBasketOne = Set("Cake", "Milk", "Cheese", "Toilet Paper")
 val setBasketTwo = Set("Bread", "Water", "Juice", "Milk", "Cheese")
 val commonItems = setBasketOne intersect setBasketTwo
 println(
 s"""
 |Shopping Basket One = ${setBasketOne.mkString(" - ")}
 |Shopping Basket Two = ${setBasketTwo.mkString(" - ")}
 |Common items are:
 """.stripMargin)

 commonItems.foreach(item => println(item.toUpperCase()))

You should see the following output when you run your Scala application in IntelliJ:

Shopping Basket One = Cake - Milk - Cheese - Toilet Paper
Shopping Basket Two = Water - Milk - Juice - Cheese - Bread
Common items are:
 
MILK
CHEESE

Problem 11: Assume a shopping basket with the following items: "Milk", "Cheese", "Donuts", "Apples", "Bananas". Represent the above items into an appropriate data structure, and thereafter define and use a value function that will remove all fruit items from the shopping basket. In other words, the shopping basket should only contain items "Milk", "Cheese" and "Donuts". Note also that the resulting output should be represented as an XML element format as shown below.
Output:
<items values= "Milk|Cheese|Donuts"></items>

Solution:


 val shoppingBasket = Seq("Milk", "Cheese", "Donuts", "Apples", "Bananas")
 val filterFruitItemsF: (String) => Boolean = (s) => s == "Apples" || s == "Bananas"
 val basketWithoutFruits = shoppingBasket.filterNot(filterFruitItemsF)
 println(basketWithoutFruits.mkString("<items values= \"", "|", "\"></items>"))

You should see the following output when you run your Scala application in IntelliJ:

<items values= "Milk|Cheese|Donuts"></items>

Problem 12: Assume the following lexical coupon codes: "A", "BB", "CCC", "DDDD", "EEEEE". Write a Scala program to create a new set of coupon codes based on the above one. The format for each coupon code should be as follows: "coupon code - i", where the number i is derived from the length of each corresponding coupon code.
Output:
A - 1
BB - 2
CCC - 3
DDDD - 4
EEEEE - 5

Solution:


 val codes = Seq("A", "BB", "CCC", "DDDD", "EEEEE")
 val codesWithLength = codes.map { code =>
   s"$code - ${code.length}"
 }

 codesWithLength.foreach(println(_))

You should see the following output when you run your Scala application in IntelliJ:

A - 1
BB - 2
CCC - 3
DDDD - 4
EEEEE - 5

Problem 13: Write a Scala program and use the Tuple type to represent items in a shopping baskets:
"T-Shirt", "Medium", 10.99
"Polo-Shirt", "Large", 4.99
"Vest", "Small", 5.99
"T-Shirt", "Small", 4.99
"T-Shirt", "Small", 4.99
You are free to use whichever data structure is appropriate to store the above-mentioned items. The Scala program should then output all "T-Shirt" items in upper case format. Note that all other items in the shopping basket should then be in the lower case format.
Output:
T-SHIRT is priced at $10.99 for the Medium size.
polo-shirt is priced at $4.99 for the Large size.
vest is priced at $5.99 for the Small size.
T-SHIRT is priced at $4.99 for the Small size.
T-SHIRT is priced at $4.99 for the Small size.

Solution:


 val item1 = Tuple3("T-Shirt", "Medium", 10.99)
 val item2 = Tuple3("Polo-Shirt", "Large", 4.99)
 val item3 = Tuple3("Vest", "Small", 5.99)
 val item4 = Tuple3("T-Shirt", "Small", 4.99)
 val item5 = Tuple3("T-Shirt", "Small", 4.99)

 val shoppingBasket = List(item1, item2, item3, item4, item5)
 shoppingBasket.foreach {
   case item if item._1 == "T-Shirt" =>
     println(s"${item._1.toUpperCase()} is priced at $$${item._3} for the ${item._2} size.")
   case item =>
     println(s"${item._1.toLowerCase()} is priced at $$${item._3} for the ${item._2} size.")
}

You should see the following output when you run your Scala application in IntelliJ:

T-SHIRT is priced at $10.99 for the Medium size.
polo-shirt is priced at $4.99 for the Large size.
vest is priced at $5.99 for the Small size.
T-SHIRT is priced at $4.99 for the Small size.
T-SHIRT is priced at $4.99 for the Small size.

Problem 14: Write a Scala program and create two Lists data structures as follows: (1) The first List will have items named "pencil", "pen", "sharpener", and (2) The second List will have items name "math book", "french book", "english book". Merge the two List data structures such that the resulting combined data structure is of type List[List[String]]. Finally, output all items with the literal " is required in the classroom.".
Output:
pencil is required in the classroom.
pen is required in the classroom.
sharpener is required in the classroom.
math book is required in the classroom.
french book is required in the classroom.
english book is required in the classroom.

Solution:


 val list1 = List("pencil", "pen", "sharpener")
 val list2 = List("math book", "french book", "english book")
 val combinedList: List[List[String]] = List(list1, list2)
 combinedList
 .flatten
 .map(_ + " is required in the classroom.")
 .foreach(println(_))

You should see the following output when you run your Scala application in IntelliJ:

pencil is required in the classroom.
pen is required in the classroom.
sharpener is required in the classroom.
math book is required in the classroom.
french book is required in the classroom.
english book is required in the classroom.

 

Working with numbers

Problem 1: Create a Scala program that defines a sequence of numbers from 100 to 110. The sequence should include the 100 starting number literal, and ends with the 110 number literal.

Output:

Number range from 100 to 110 inclusive = 100 101 102 103 104 105 106 107 108 109 110

Solution:


val numberRange = 100 to 110
print("Number range from 100 to 110 inclusive = ")
numberRange.foreach(number => print(s"$number "))

You should see the following output when you run your Scala application in IntelliJ:


Number range from 100 to 110 inclusive = 100 101 102 103 104 105 106 107 108 109 110 

 

Problem 2: Create a Scala program to represent a List with a sequence of number from 100 to 150. You should omit the last numeric item of 150, and each numeric item should have a 10 numeric interval. You should then calculate the sum of all the numeric items in the List.
Output:
Elements of Vector from 100 to 150, excluding the 150 number literal = 100, 110, 120, 130, 140
Sum for elements in the List = 600

Solution:


val listRange = List.range(100, 150, 10)
val listRangeAsStr = listRange.mkString(", ")
println(s"Elements of List from 100 to 150, excluding the 150 number literal = $listRangeAsStr")
println(s"Sum for elements in the List = ${listRange.sum}")

You should see the following output when you run your Scala application in IntelliJ:


Elements of List from 100 to 150, excluding the 150 number literal = 100, 110, 120, 130, 140
Sum for elements in the List = 600

 

Problem 3: Create a Vector with the following numeric items: 0, 10, 20, 47, -2, 99, -98. Write a Scala program to find the smallest and the largest numeric item in the Vector.
Output:
The smallest item in the Vector = -98
The largest item in the Vector = 99

Solution:


val vecNumbers = Vector(0, 10, 20, 47, -2, 99, -98)
println(s"The smallest item in the Vector = ${vecNumbers.min}")
println(s"The largest item in the Vector = ${vecNumbers.max}")

You should see the following output when you run your Scala application in IntelliJ:


The smallest item in the Vector = -98
The largest item in the Vector = 99

 

Problem 4: Let us assume two data structures to represent the following numerals: (1) 1, 3, 5, 10, 20 and (2) 20, 17, 18, 99, 0. Write a Scala program to find the number literals that are in the first data structure, but not in the other one. Conversely, also find the number literals that are in the second data structure, but not in the first one. You can use whichever data structure from the standard Scala collection types.
Output:
Number literals in set one but not in set two = HashSet(5, 10, 1, 3)
Number literals in set two but not in set one = HashSet(0, 17, 18, 99)

Solution:


 val setOne = Set(1, 3, 5, 10, 20)
 val setTwo = Set(20, 17, 18, 99, 0)

 val diffSetOneVSSetTwo = setOne -- setTwo
 val diffSetTwoVSSetOne = setTwo -- setOne

 println(s"Number literals in set one but not in set two = $diffSetOneVSSetTwo")
 println(s"Number literals in set two but not in set one = $diffSetTwoVSSetOne")

You should see the following output when you run your Scala application in IntelliJ:


Number literals in set one but not in set two = HashSet(5, 10, 1, 3)
Number literals in set two but not in set one = HashSet(0, 17, 18, 99)

Problem 5: Write a Scala program and use an appropriate data structure to represent the following number literal: 99.5, 100.0, 50.0, 55.0, 70.0, 100.0, -1.0. Assume a second data structure with the following number literals: 10.0, 20.0, 30.0, 40.0, 50.0. Join the two data structures together, and thereafter find the lowest and the largest number literal from the combined number literals.
Output:
Combined number literals = List(99.5, 100.0, 50.0, 55.0, 70.0, 100.0, -1.0, 10, 20, 30, 40, 50)
Lowest number literal = -1.0
Largest number literal = 100.0

Solution:


  val seqOne = Seq(99.5, 100.0, 50.0, 55.0, 70.0, 100.0, -1.0)
  val seqTwo = Seq(10.0, 20.0, 30.0, 40.0, 50.0)
  val combinedNumbers = seqOne ++ seqTwo

  implicit val doubleOrdering = Ordering.Double.TotalOrdering
  val minNumber = combinedNumbers.min
  val maxNumber = combinedNumbers.max

  println(s"Combined number literals = $combinedNumbers")
  println(s"Lowest number literal = $minNumber")
  println(s"Largest number literal = $maxNumber")

You should see the following output when you run your Scala application in IntelliJ:


Combined number literals = List(99.5, 100.0, 50.0, 55.0, 70.0, 100.0, -1.0, 10.0, 20.0, 30.0, 40.0, 50.0)
Lowest number literal = -1.0
Largest number literal = 100.0

 

Problem 6: Write a Scala value function that will always return true for all numeric literals that match to some magic number of, say, 77. That is, the value function should of course output false for all other numeric values. Then, use an appropriate data structure to capture the following numeric values: 10, 77, 90, 50, 100, 110. Use the value function to verify if the magic number of 77 is part of the aforementioned numeric values.

Output: Does magic number 77 exist within the numerical value of 10,77,90,50,100,110? TRUE

Solution:


 val findMagicNumberF: (Int) => Boolean = (i) => i == 77
 val numberSeq = Seq(10, 77, 90, 50, 100, 110)
 val boolMagicNumberExists = numberSeq.exists(findMagicNumberF)
 println(s"Does magic number 77 exist within the numerical value of ${numberSeq.mkString(",")}? ${boolMagicNumberExists.toString.toUpperCase()}")

You should see the following output when you run your Scala application in IntelliJ:


Does magic number 77 exists within the numerical value of 10,77,90,50,100,110? TRUE

 

Problem 7: Write a Scala program and use an appropriate data structure to represent the following numerical values: 2, 8, 19, 20, 25, 50, 100, 10. Define a value function that will identify whether a given numeric value is divisible by 2. Using this value function, and starting from the left hand side to the right hand side, remove all elements from the abovementioned data structure that are factors of two. But, your program should stop removing elements as soon as a number literal is not a factor of two.
Output:
Starting from left to right, removing items that are factors of two, and will
stop execution on the first element that is not divisible by two:
19
20
25
50
100
10

Solution:


 val numberSeq = Seq(2, 8, 19, 20, 25, 50, 100, 10)
 val divByTwoF: (Int) => Boolean = (i) => i % 2 == 0
 println(
 """
 |Starting from left to right, removing items that are factors of two, and will
 |stop execution on the first element that is not divisible by two:
 """.stripMargin)
 numberSeq
 .dropWhile(divByTwoF)
 .foreach(println(_))

You should see the following output when you run your Scala application in IntelliJ:


Starting from left to right, removing items that are factors of two, and will
stop execution on the first element that is not divisible by two:
 
19
20
25
50
100
10

 

Problem 8: Write a Scala program and use a Sequence data structure to store a combination of names to ages as follows:
Seq(
"James", 7,
"Andy", 8,
"Tommy", 10,
"Bob", 13,
"Sam", 10
)
Note that the above elements are not in a key value pair format. They are instead in a comma separated format within the Sequence. From the above Sequence, extract the age number literal, and use this to find the sum of ages.
Output:
Combined sequence of names to ages = James,7,Andy,8,Tommy,10,Bob,13,Sam,10
Sum of ages = 48

Solution:


 val nameAndAge = Seq(
 "James", 7,
 "Andy", 8,
 "Tommy", 10,
 "Bob", 13,
 "Sam", 10
 )

 val ageSeq = nameAndAge.collect {
 case age: Int => age
 }

 println(s"Combined sequence of names to ages = ${nameAndAge.mkString(",")}")
 println(s"Sum of ages = ${ageSeq.sum}")

You should see the following output when you run your Scala application in IntelliJ:


Combined sequence of names to ages = James,7,Andy,8,Tommy,10,Bob,13,Sam,10
Sum of ages = 48

 

Problem 9: Write a Scala program and list all the odd numbers between 300 and 350. As a tip, there is no need to manually create a data structure to represent all the number literals, such as, 300, 301, 302, etc. Instead, use the handy Range type to help you create number literals between 300 and 350. As a second tip, experiment with the handy collection functions in Scala that can help you avoid boiler-plate code to solve this particular problem.
Output:
Odd number between 300 and 340 =
301||303||305||307||309||311||313||315||317||319||321||323||325||327||329||331||333||335||337||339

Solution:


 val oddNumbers = (300 to 340)
 .filter(_ %2 != 0)
 .take(20)
 .toList
 .mkString("||")

 println("Odd numbers between 300 and 340 = ")
 println(oddNumbers)

You should see the following output when you run your Scala application in IntelliJ:


Odd number between 300 and 340 = 
301||303||305||307||309||311||313||315||317||319||321||323||325||327||329||331||333||335||337||339

 

Classes, Functions, Traits

Problem 1: Write a Scala program and use the Tuple type to represent a shopping cart item with the following properties: a name, a price, and a quantity bought. Thereafter, use a case class to represent the above shopping cart item. You can use the following shopping cart item as an example: A packet of rice at $10.99 and quantity bought is 5.
Output:
A packet of rice is currently priced at $10.99, and the customer bought 5.
A packet of rice is currently priced at $10.99, and the customer bought 5.

Solution:


 val tupleItem = ("packet of rice", 10.99, 5)
 println(s"A ${tupleItem._1} is currently priced at $$${tupleItem._2}, and the customer bought ${tupleItem._3}.")

 final case class ShoppingCartItem(name: String, price: Double, qtyBought: Int)
 val cartItem = ShoppingCartItem(
 name = "packet of rice",
 price = 10.99,
 qtyBought = 5)
 println(s"A ${cartItem.name} is currently priced at $$${cartItem.price}, and the customer bought ${cartItem.qtyBought}.")

You should see the following output when you run your Scala application in IntelliJ:


A packet of rice is currently priced at $10.99, and the customer bought 5.
A packet of rice is currently priced at $10.99, and the customer bought 5.

 

Problem 2: Write a Scala program and use a case class to define a shopping cart item. Each shopping cart item should have the following properties, namely, a name, a price, and a quantity bought. Create three shopping cart items for the following items:
10 vanilla ice cream at $2.99 each
3 chocolate biscuits at $3.99 each
5 cupcakes at $4.99 each

Use an appropriate data structure to store the above-mentioned shopping cart items. Thereafter, define and use a method that will print out all items from a given shopping cart.
Output:
10 vanilla ice cream at $2.99 each
3 chocolate biscuits at $3.99 each
5 cupcakes at $4.99 each

Define also another method that given a shopping cart basket will only output vanilla ice cream products. A generic message, such as, "Found another item", will be the output for all other items.
Output:
Found another item.
Found another item.
Found a cupcake item.

Solution:


 final case class ShoppingCartItem(name: String, price: Double, qtyBought: Int)

 val item1 = ShoppingCartItem("vanilla ice cream", 2.99, 10)
 val item2 = ShoppingCartItem("chocolate biscuits", 3.99, 3)
 val item3 = ShoppingCartItem("cupcakes", 4.99, 5)
 val basket = List(item1, item2, item3)

 def printCartItems(basket: List[ShoppingCartItem]): Unit = {
   basket.foreach { item =>
     println(s"${item.qtyBought} ${item.name} at $$${item.price} each")
   }
 }

 printCartItems(basket)


 def printIceCream(basket: List[ShoppingCartItem]): Unit = {
   basket.foreach { {
     case ShoppingCartItem("cupcakes", _, _) => println(s"Found a cupcake item.")
     case ShoppingCartItem(_,_,_) => println("Found another item.")
     }
   }
 }

 printIceCream(basket)

You should see the following output when you run your Scala application in IntelliJ:


10 vanilla ice cream at $2.99 each
3 chocolate biscuits at $3.99 each
5 cupcakes at $4.99 each
Found another item.
Found another item.
Found a cupcake item.

 

Problem 3: Write a Scala program that defines a method with an integer input parameter. You can use this method to produce the following output.

Output:

10 as String literal = 10
Create another method similar to the above-mentioned, but this particular method should accept an arbitrary integer input parameters as opposed to just one. You can then use this method to produce the following output.

Output:
10, 11, 12 as String literals = 10 :: 11 :: 12

Solution:


 def numberToString(num: Int): String = {
   num.toString
 }

 println(s"10 as String literal = ${numberToString(10)}")


 def numbersToString(numbers: Int*): String = {
   numbers.mkString(" :: ")
 }

 println(s"10, 11, 12 as String literals = ${numbersToString(10, 11, 12)}")

You should see the following output when you run your Scala application in IntelliJ:


10 as String literal = 10
10, 11, 12 as String literals = 10 :: 11 :: 12

 

Problem 4: Write a Scala program and create a class that will be called BasketValidator. This class will have a "validate" method that will accept an input "item" whose type need to be generic. The "validate" method should identify items of type String, Double and Int. For String types, it will output "Found a valid item = [the item]". For Double types, it will output: "Item [the item] of type Double is not valid". For Int types, it will output: "Item [the item] of type Int is not valid". For all other types, it should output: "Item [the item] should be removed from the basket.". Besides the "BasketValidator" class, you should also define its companion object, along with the relevant apply() method. You can use the following List data structure as your input to the above-mentioned "validate" method: List("Cupcake", 2.99, 100L, 7, "Ice Cream").
Output:
Found a valid item = Cupcake
Item 2.99 of type Double is not valid.
Item 100 should be removed from the basket.
Item 7 of type Int is not valid.
Found a valid item = Ice Cream

Solution:


 class BasketValidator() {
   def validate[T](item: T): Unit = {
     item match {
       case i: String =>
         println(s"Found a valid item = $i")
       case i: Double =>
         println(s"Item $i of type Double is not valid.")
       case i: Int =>
         println(s"Item $i of type Int is not valid.")
       case _ =>
         println(s"Item $item should be removed from the basket.")
       }
     }
 }

 object BasketValidator {
   def apply(): BasketValidator = new BasketValidator()
 }

 val basket = List("Cupcake", 2.99, 100L, 7, "Ice Cream")
 basket.foreach(BasketValidator().validate(_))

You should see the following output when you run your Scala application in IntelliJ:


Found a valid item = Cupcake
Item 2.99 of type Double is not valid.
Item 100 should be removed from the basket.
Item 7 of type Int is not valid.
Found a valid item = Ice Cream

 

Problem 5: Write a Scala program and define the following Partial Functions. The first will be called "nameIsJohn" and it will have a String input parameter, and will output true only for the String literal "John". This partial function will not have any other matching clauses. The second partial function will be called "nameIsJoe" and it will have a String input parameter, and will output true only for the String literal "Joe". Similar to the previous partial function, it will not have any other matching clauses. The third partial function will be called "nameIsJackOrJill" and it will output true for String literals of "Jack" or "Jill". Likewise to the previous partial functions, it will not have any other matching clauses. The final partial function will output false for all String inputs. Using the above-mentioned partial functions, combined them into a single partial function that match this particular type: PartialFunction[String, Boolean]. Using the combined single partial function, test the following name inputs to produce the resulting output as shown below.
Output:
Name John is valid = true
Name Jack is valid = true
Name Bob is valid = false

 

Solution:


 val nameIsJohn: PartialFunction[String, Boolean] = {
   case "John" => true
 }

 val nameIsJoe: PartialFunction[String, Boolean] = {
   case "Joe" => true
 }

 val nameIsJackOrJill: PartialFunction[String, Boolean] = {
   case "Jack" | "Jill" => true
 }

 val invalidName: PartialFunction[String, Boolean] = {
   case _ => false;
 }

 val nameValidator = nameIsJohn orElse nameIsJoe orElse nameIsJackOrJill orElse invalidName
 println(s"Name John is valid = ${nameValidator("John")}")
 println(s"Name Jack is valid = ${nameValidator("John")}")
 println(s"Name Bob is valid = ${nameValidator("Bob")}")

You should see the following output when you run your Scala application in IntelliJ:


Name John is valid = true
Name Jack is valid = true
Name Bob is valid = false

 

Problem 6: Write a Scala program which defines a method named "toUpper" and it accepts a String as input parameter that is then formatted to upper case as output. Define another method named "toLower" which accepts a String as input parameter and formats the input to lower case as output. Define another method named "formatNames" which also has an input String called "name". This method however has a parameter group which accepts a functions with an input of type String and also outputs a String. This particular function will be used to apply the given format to the "name" input. You can use the test inputs for, say, "Bob", "Joe", and "Jack", and make sure that the output is as shown below.
Output:
BOB
joe
JACK

Solution:


 def toUpper(s: String): String = {
   s.toUpperCase()
 }

 def toLower(s: String): String = {
   s.toLowerCase()
 }

 def formatNames(name: String)(func: String => String): String = {
   func(name)
 }

 println(formatNames("Bob")(toUpper(_)))
 println(formatNames("Joe")(toLower(_)))
 println(formatNames("Jack")(toUpper(_)))

You should see the following output when you run your Scala application in IntelliJ:


BOB
joe
JACK

 

Problem 7: Write a Scala program and use a case class structure to represent the following students: (1) John who is 7 years old, (2) Jack who is 13 years old, (3) Joe who is 15 years old, (4) Jill who is 15 years old, and (5) James who is 11 years old. Use an appropriate data structure, and store the above-mentioned students. Next, write a recursive method that will walk-through your data structure and output true for the first student that is of 15 years old. If no students are found matching this particular criteria, the recursive function should return false.
Output:
Student(John,7)
Student(Jack,13)
Student(Joe,15)
Student(Jill,15)
Student(James,11)
Is there a student who is 15 years old = true

Solution:


 final case class Student(name: String, age: Int)

 val john = Student(name = "John", age = 7)
 val jack = Student(name = "Jack", age = 13)
 val joe = Student(name = "Joe", age = 15)
 val jill = Student(name = "Jill", age = 15)
 val james = Student(name = "James", age = 11)

 val students = Vector(john, jack, joe, jill, james)
 println(students.mkString("\n"))


 @annotation.tailrec
 def findStudentAge15(student: Student, students: Vector[Student], index: Int): Boolean = {
   if(students.length == index)
     false
   else if (students(index).age == student.age)
     true
   else
     findStudentAge15(student, students, index + 1)
 }


 val foundStudent = findStudentAge15(Student("", 15), students, 0)
 println(s"Is there a student who is 15 years old = $foundStudent")

You should see the following output when you run your Scala application in IntelliJ:


Student(John,7)
Student(Jack,13)
Student(Joe,15)
Student(Jill,15)
Student(James,11)
Is there a student who is 15 years old = true

 

Problem 8: Write a Scala program and use an abstract class to define a student that is required to have a name of type String, and an age of type Int. Each Student type should also have a printName() method with Unit as the return type. Thereafter create two sub classes of the Student class, namely, a PrimaryStudent, and a SecondaryStudent. Each sub class should provide its own implementation of the printName() method. You should also define the respective companion objects for the PrimaryStudent and the SecondaryStudent types. Next, create the following students:
- John who is a primary student and is 8 years old.
- Jill who is a primary student and is 10 years old.
- James who is a secondary student and is 13 years old.
- Joe who is a secondary student and is 14 years old.
- Jack who is a secondary student and is 11 years old.
Next, use an appropriate data structure to store the above-mentioned students. And, create a method name listStudents that will have input your collection of students and call the printName() name for each one.
Output:
name = Jill, age = 10
name = John, age = 8
name = Jack, age = 11
name = James, age = 13
name = Joe, age = 14

Solution:


 abstract class Student(name: String, age: Int) {
   def printName(): Unit
 }

 class PrimaryStudent(name: String, age: Int) extends Student(name, age) {
   override def printName(): Unit = println(s"name = $name, age = $age")
 }

 object PrimaryStudent {
   def apply(name: String, age: Int): PrimaryStudent = new PrimaryStudent(name, age)
 }

 class SecondaryStudent(name: String, age: Int) extends Student(name, age) {
   override def printName(): Unit = println(s"name = $name, age = $age")
 }

 object SecondaryStudent {
   def apply(name: String, age: Int): SecondaryStudent = new SecondaryStudent(name, age)
 }


 val pStudentJohn = PrimaryStudent("John", 8)
 val pStudentJill = PrimaryStudent("Jill", 10)
 val sStudentJames = SecondaryStudent("James", 13)
 val sStudentJoe = SecondaryStudent("Joe", 14)
 val sStudentJack = SecondaryStudent("Jack", 11)

 val students = List(pStudentJill, pStudentJohn, sStudentJack, sStudentJames, sStudentJoe)

 def listStudents(students: List[Student]): Unit = {
   students.foreach(student => student.printName())
 }

 listStudents(students)

You should see the following output when you run your Scala application in IntelliJ:


name = Jill, age = 10
name = John, age = 8
name = Jack, age = 11
name = James, age = 13
name = Joe, age = 14

 

Problem 9: Write a Scala program and use a case class to represent a Student with a name property of type String, an age property of type Int, and an optional favoriteSnack property of type String. Use the above case class and create the following student objects:
- Jack who is 15 years old and whose favorite snack is cupcake.
- Jill who is 10 years old and whose favorite snack is ice cream.
- Joe who is 7 years old and whose favorite snack is nothing!
- James who is 10 years old and whose favorite snack is chocolate.
- John who is 11 years old and whose favorite snack is nothing!
The student Joe now wants to add his favorite snack and it is a cupcake. Next, define a method named listStudents that will have input a List of Student types and output the details of each students as shown below. You should of course also create a List data structure to store the above-mentioned students.
Output:
name = Jack is 15 years old. Favorite snack is cupcake

name = Jill is 10 years old. Favorite snack is ice cream

name = Joe is 7 years old. Favorite snack is cupcake

name = James is 10 years old. Favorite snack is chocolate

name = John is 11 years old. Favorite snack is nothing!

Solution:



 final case class Student(name: String, age: Int, favoriteSnack: Option[String] = None)

 val jack = Student("Jack", 15, Some("cupcake"))
 val jill = Student("Jill", 10, Some("ice cream"))
 val joe = Student("Joe", 7, None)
 val james = Student("James", 10, Some("chocolate"))
 val john = Student("John", 11, None)


 val joeUpdated = joe.copy(favoriteSnack = Some("cupcake"))


 def listStudents(students: List[Student]): Unit = {
   students.foreach{ student=>
     print(s"name = ${student.name} is ${student.age} years old. ")
     print(s"Favorite snack is ${student.favoriteSnack.getOrElse(" nothing!")}")
     println("\n")
   }
 }

 listStudents(List(jack, jill, joeUpdated, james, john))
}

You should see the following output when you run your Scala application in IntelliJ:



name = Jack is 15 years old. Favorite snack is cupcake

name = Jill is 10 years old. Favorite snack is ice cream

name = Joe is 7 years old. Favorite snack is cupcake

name = James is 10 years old. Favorite snack is chocolate

name = John is 11 years old. Favorite snack is nothing!

 

Problem 10: Write a Scala program to simulate a very basic car inventory. In doing so, use a case class to represent a Car type that needs to have a name property of type String, and a price property of type Double. Thereafter you should define an aliased type named CarStock that will in effect, alias, Scala's Tuple2 type and represent a tuple or pair of Car and Int. The former is obviously the above-mentioned Car type, and the Int type represents the current stock inventory for a particular Car item. You should then define a create the following car stock items, and use an appropriate data structure from Scala's Collection types.
- a vw passat with price of 10,000 and stock of 100
- a vw golf with price of 12,000 and stock of 50
- a bmw 3 series with price of 20,000 and stock of 200
- a bmw 5 series with price of 50,000 and stock of 75
- a mazda 3 series with price of 15,000 and stock of 49
Create a method named orderByLowestStock which will have as input the Collection data structure of cars to stock, and will order the car items from the collection by the lowest or minimum stock quantity.
Output:

Cars sorted by lowest stock:
mazda 3 stock = 49
vw golf stock = 50
bmw 5 series stock = 75
vw passat stock = 100
bmw 3 series stock = 200

Solution:


 final case class Car(name: String, price: Double)

 type CarStock = Tuple2[Car, Int]

 val vwPassatStock = new CarStock(Car("vw passat", 10000), 100)
 val vwGolfStock = new CarStock(Car("vw golf", 12000), 50)
 val bmw3Stock = new CarStock(Car("bmw 3 series", 20000), 200)
 val bmw5Stock = new CarStock(Car("bmw 5 series", 50000), 75)
 val mazdaStock = new CarStock(Car("mazda 3", 15000), 49)

 val carInventory = List(vwPassatStock, vwGolfStock, bmw3Stock, bmw5Stock, mazdaStock)

 def orderByLowestStock(inventory: List[CarStock]): Unit = {
   inventory
     .sortBy(_._2)
     .foreach { case (car, stock) =>
       println(s"${car.name} stock = $stock")
     }
 }

 println("Cars sorted by lowest stock: ")
 orderByLowestStock(carInventory)

You should see the following output when you run your Scala application in IntelliJ:


Cars sorted by lowest stock: 
mazda 3 stock = 49
vw golf stock = 50
bmw 5 series stock = 75
vw passat stock = 100
bmw 3 series stock = 200

 

Problem 11: Write a Scala program to represent a basic car inventory. You should use a case class to model a Car object with a name property of type String, and a price property of type Double. Next, you should use an implicit function that defines a uuid method for each Car object, but without manually modifying the above Car type and definition. In doing so, you can define, say, a CarUUID class with has a constructor parameter for the Car type, and implements a uuid method. The actually uuid heuristics can be very basic, such as, an output with the combined car name and the car name's hashCode. To verify your uuid method, you can define the following car sample:
- a bmw 3 series priced at 20,000
- a bmw 5 series priced at 50,000
- a vw passat priced at 10,000
- a vw golf priced at 12,000
- a mazda 3 priced at 15,000
Use an appropriate data structure from Scala's collection types to store the above car sample. Finally, you should output each car's uuid method as shown below.
Output:
car uuid = bmw 3 series - -2034747624
car uuid = bmw 5 series - 1450873046
car uuid = vw passat - 44703299
car uuid = vw golf - 790852193
car uuid = mazda 3 - 846423990

Solution:


 final case class Car(name: String, price: Double)

 class CarUUID(car: Car) {
   def uuid: String = s"car uuid = ${car.name} - ${car.name.hashCode}"
 }

 object CarExtensions {
   implicit def uuid(car: Car): CarUUID = new CarUUID(car)
 }

 import CarExtensions._
 val bmw3 = Car("bmw 3 series", 20000)
 val bmw5 = Car("bmw 5 series", 50000)
 val vwPassat = Car("vw passat", 10000)
 val vwGolf = Car("vw golf", 12000)
 val mazda3 = Car("mazda 3", 15000)

 val cars = List(bmw3, bmw5, vwPassat, vwGolf, mazda3)
 cars.foreach(car => println(car.uuid))

You should see the following output when you run your Scala application in IntelliJ:


car uuid = bmw 3 series - -2034747624
car uuid = bmw 5 series - 1450873046
car uuid = vw passat - 44703299
car uuid = vw golf - 790852193
car uuid = mazda 3 - 846423990

Problem 12: This exercise is very similar to the previous one. You will write a basic Scala program to represent a car inventory. You should use a case class to model a Car object with a name property of type String, and a price property of type Double. Next, you should extend - so to speak - the Car type by having a uuid method, but without modifying the source code of the Car class. Furthermore, and unlike the previous scala practice exercise, you should use an implicit class as opposed to an implicit function to solve this particular problem set. The uuid method should have an output with the combined car name and the car name's hashCode. To verify your uuid method, you can define the following car sample:
- a bmw 3 series priced at 20,000
- a bmw 5 series priced at 50,000
- a vw passat priced at 10,000
- a vw golf priced at 12,000
- a mazda 3 priced at 15,000
Use an appropriate data structure from Scala's collection types to store the above car sample. Finally, you should output each car's uuid method as shown below.
Output:
car uuid = bmw 3 series - -2034747624
car uuid = bmw 5 series - 1450873046
car uuid = vw passat - 44703299
car uuid = vw golf - 790852193
car uuid = mazda 3 - 846423990

Solution:


 final case class Car(name: String, price: Double)

 object CarExtensions {
   implicit class CarUUID(car: Car) {
     def uuid: String = s"car uuid = ${car.name} - ${car.name.hashCode}"
   }
 }

 import CarExtensions._
 val bmw3 = Car("bmw 3 series", 20000)
 val bmw5 = Car("bmw 5 series", 50000)
 val vwPassat = Car("vw passat", 10000)
 val vwGolf = Car("vw golf", 12000)
 val mazda3 = Car("mazda 3", 15000)

 val cars = List(bmw3, bmw5, vwPassat, vwGolf, mazda3)
 cars.foreach(car => println(car.uuid))

You should see the following output when you run your Scala application in IntelliJ:


car uuid = bmw 3 series - -2034747624
car uuid = bmw 5 series - 1450873046
car uuid = vw passat - 44703299
car uuid = vw golf - 790852193
car uuid = mazda 3 - 846423990

Problem 13: Write a Scala program that will model a very basic student to school abstract data type. For instance, define a base abstract class named Student with a name property of type String, and an age property of type Int. This particular base class should also define the method signature for a method named studentId() that will have no return type, and no implementation, as the actual implementation details will be left to the subsequent sub-classes. As such, create two sub-classes of the Student abstract class, namely, a PrimarySchoolStudent, and a SecondarySchoolStudent, and you can conveniently use a case class to represent these two sub-classes. Next, create a class named PrimarySchool that has a constructor input parameter for a List of students and should also further restrict and accept only List items that are derived from the abstract Student class. For your Scala program, you can thereafter define the following students, and while explicitly defining their types:
Jill is a primary school student of 8 years old and whose type should be the PrimarySchoolStudent class.
Joe is a primary school student of 7 years old and whose type should be the PrimarySchoolStudent class.
Jack is a secondary school student of 15 years old and whose type should be the SecondarySchoolStudent class.
James is a secondary school student of 10 years old and whose type should be the SecondarySchoolStudent class.
John is a secondary school student of 11 years old and whose type should be the SecondarySchoolStudent class.
Use a List data structure from Scala's collection type to store the above students. Next, create an instance of the School class and call its printStudents() method that will output both the primary and secondary students. As a hint or tip, you should keep in mind that the type parameter for the School class should make use of Scala's built-in variance features in order to restrict only types and sub-types of the abstract Student class.
Output:
[SecondarySchoolStudent] - name: Jack - age: 15
[SecondarySchoolStudent] - name: James - age: 10
[SecondarySchoolStudent] - name: John - age: 11
[PrimarySchoolStudent] - name: Jill - age: 8
[PrimarySchoolStudent] - name: Joe - age: 7

Solution:


 abstract class Student(name: String, age: Int) {
   def studentId(): Unit
 }

 case class PrimarySchoolStudent(name: String, age: Int) extends Student(name, age) {
   override def studentId(): Unit = {
     println(s"[${this.getClass.getSimpleName}] - name: $name - age: $age")
   }
 }


 case class SecondarySchoolStudent(name: String, age: Int) extends Student(name, age) {
   override def studentId(): Unit = {
     println(s"[${this.getClass.getSimpleName}] - name: $name - age: $age")
   }
 }


 class School[+S <: Student](students: List[S]) {
   def printStudents(): Unit = {
     students.foreach(_.studentId())
   }
 }


 val jill: PrimarySchoolStudent = PrimarySchoolStudent("Jill", 8)
 val joe: PrimarySchoolStudent = PrimarySchoolStudent("Joe", 7)
 val jack: SecondarySchoolStudent = SecondarySchoolStudent("Jack", 15)
 val james: Student = SecondarySchoolStudent("James", 10)
 val john: Student = SecondarySchoolStudent("John", 11)

 val students = List(jack, james, john, jill, joe)

 val primarySchool = new School(students)
 primarySchool.printStudents()

You should see the following output when you run your Scala application in IntelliJ:


[SecondarySchoolStudent] - name: Jack - age: 15
[SecondarySchoolStudent] - name: James - age: 10
[SecondarySchoolStudent] - name: John - age: 11
[PrimarySchoolStudent] - name: Jill - age: 8
[PrimarySchoolStudent] - name: Joe - age: 7

Problem 14: Write a Scala program that defines a base abstract class to model a Vehicle type that has a public make property of type String. This particular Vehicle base class will be extended by two sub-types, namely, a Car, and a Bike, case class, and will wire accordingly the make property of type String from the Vehicle base class. Next, create a singleton object named VehicleReport that will define a printVehicles method that will have as input a List of Vehicle types, and any of its sub-types or sub-classes. The printVehicles() method will simply iterate through each of the Vehicle type and output its corresponding make property. Note that the printVehicles() method will have no return type defined as such. You can use the following vehicle samples to model your data points:
a car whose make is: bmw 3 series
a car whose make is: vw golf
a bike whose make is: bmw g 310 r bike
a bike whose make is: fire storm bike
Use the List data structure from Scala's collection type and store the above-mentioned vehicles. And, finally, call the VehicleReport's printVehicles() method by passing through your collection of vehicles as defined above.
Output:
bmw 3 series
vw golf
bmw g 310 r bike
fire storm bike

Solution:


 abstract class Vehicle(val make: String)

 case class Car(override val make: String) extends Vehicle(make)

 case class Bike(override val make: String) extends Vehicle(make)

 object VehicleReport {
   def printVehicles[V <: Vehicle](vehicles: List[V]): Unit = {
     vehicles.foreach(v => println(v.make))
   }
 }


 val bmwCar = Car("bmw 3 series")
 val vwCar = Car("vw golf")
 val bmwBike = Bike("bmw g 310 r bike")
 val hondaBike = Bike("fire storm bike")
 val vehicles = List(bmwCar, vwCar, bmwBike, hondaBike)
 VehicleReport.printVehicles(vehicles)

You should see the following output when you run your Scala application in IntelliJ:


bmw 3 series
vw golf
bmw g 310 r bike
fire storm bike

Problem 15: Write a Scala program and use an abstract class to model a Donut pastry with a name property of type String, and having also a printName() method that does not have a return type. Use Scala's case class construct to define two sub-classes of the abstract Donut class to define two additional types, namely, a VanillaDonut, and a GlazedDonut. Next, create a class that accepts an upper type bound for a Donut type as its constructor argument. This particular Pastry class will further have a name() method that basically calls a given pastry's printName() method - that is of course derived from the upper type bound constraint that is applied to the Pastry's constructor argument. With the above classes defined, create two instances of Pastry, one with an upper type bound constraint of VanillaDonut, whereas the other, is bound to the abstract Donut type. You will have to respectively pass-through corresponding objects - that is, a VanillaDonut, and a GlazedDonut - to the two Pastry instances, and thereafter call the Pastry's name() method.

Output:
Vanilla Donut
Glazed Donut

Solution:

 abstract class Donut(name: String) {
   def printName(): Unit
 }

 case class VanillaDonut(name: String) extends Donut(name) {
   override def printName(): Unit = println(name)
 }

 case class GlazedDonut(name: String) extends Donut(name) {
   override def printName(): Unit = println(name)
 }

 val vanillaDonut = VanillaDonut("Vanilla Donut")
 val glazedDonut = GlazedDonut("Glazed Donut")


 // Upper Type Bounds
 class Pastry[P <: Donut](pastry: P){
   def name(): Unit = pastry.printName()
 }

 val vanillaPastry = new Pastry[VanillaDonut](vanillaDonut)
 vanillaPastry.name()

 val glazedPastry = new Pastry[Donut](glazedDonut)
 glazedPastry.name()

You should see the following output when you run your Scala application in IntelliJ:


Vanilla Donut
Glazed Donut

Problem 16: Write a Scala program and use a case class to model a Lollipop type with a name property of type String. Thereafter, create an object, or instance, of the Lollipop type. Using the types from the previous scala exercises - that is, the Donut, VanillaDonut, GlazedDonut, and Pastry classes - try to define a Pastry object that has an upper type bound to the Lollipop type. What you will observe is that given the upper type bound constraint of P <: Donut on the Pastry type's constructor argument, you will receive a compile time error because a Pastry of type Lollipop does not match P <: Donut - in other words, a Lollipop is not a sub-type of the base Donut type. Next, create a ShoppingCart class that has an addCartItem() method with a parameter named item that is represented as a lower type bound with respect to the earlier VanillaDonut type. The method should have no return type, but will output the following details regarding the item parameter. The output below is obviously the result of creating an object, or instance, of the ShoppingCart class and calling the addCartItem() multiple times with values of VanillaDonut, GlazedDonut, Lollipop, and a String. The String is of course to illustrate the point that you have to understand the lower type bound constraint, and determine that it is right for your particular use case. In other words, unlike upper type bounds, the lower type bounds may be too unrestrictive, such as in the example below, where we are able to pass-through a String value to the addCartItem() method.
Output:
Adding VanillaDonut(Vanilla Donut) to shopping cart
VanillaDonut
Adding GlazedDonut(Glazed Donut) to shopping cart
GlazedDonut
Adding Lollipop(Lollipop) to shopping cart
Lollipop
Adding oops something is not right! to shopping cart
String

Solution:

 abstract class Donut(name: String) {
   def printName(): Unit
 }

 case class VanillaDonut(name: String) extends Donut(name) {
   override def printName(): Unit = println(name)
 }

 case class GlazedDonut(name: String) extends Donut(name) {
   override def printName(): Unit = println(name)
 }

 val vanillaDonut = VanillaDonut("Vanilla Donut")
 val glazedDonut = GlazedDonut("Glazed Donut")


 // Upper Type Bounds
 class Pastry[P <: Donut](pastry: P){
   def name(): Unit = pastry.printName()
 }

 val vanillaPastry = new Pastry[VanillaDonut](vanillaDonut)
 vanillaPastry.name()

 val glazedPastry = new Pastry[Donut](glazedDonut)
 glazedPastry.name()

case class Lollipop(name: String)

 val lollipop = new Lollipop("Lollipop")

 // Compile time error because a Lollipop type does not match the P <: Donut constraint
 // val lollipopPastry = new Pastry[Lollipop](lollipop)

 class ShoppingCart {
   def addCartItem[I >: VanillaDonut](item: I): Unit = {
     println(s"Adding $item to shopping cart")
     println(item.getClass.getSimpleName)
   }
 }


 val shoppingCart = new ShoppingCart()
 shoppingCart.addCartItem(vanillaDonut)
 shoppingCart.addCartItem(glazedDonut)
 shoppingCart.addCartItem(lollipop)
 shoppingCart.addCartItem("oops something is not right!")

You should see the following output when you run your Scala application in IntelliJ:


Vanilla Donut
Glazed Donut
Adding VanillaDonut(Vanilla Donut) to shopping cart
VanillaDonut
Adding GlazedDonut(Glazed Donut) to shopping cart
GlazedDonut
Adding Lollipop(Lollipop) to shopping cart
Lollipop
Adding oops something is not right! to shopping cart
String

Problem 17: Write a Scala program and use a case class to represent a CartItem object that has a name property of type String, a quantity property of type Int, and a price property of type Double. Next, create a singleton object named ShoppingCart and define a totalCost() method that has two parameters - the first is the above CartItem type, and the second is a couponCode of type String, but which is optional. The return type of the totalCost() method will be a Double type. In the body of the totalCost() method, use pattern matching and apply a 10% discount to the total cost for a given CartItem for a coupon code of "COUPON_1234". If there is no couponCode parameter, then the total cost for a given CartItem will be basically the associated price of the CartItem multiplied by its quantity. Next, create an object, or instance, of the CarItem to represent a Chocolate item, with 10 quantity at a price of $2.50 each.
Output:
Calculating cost for Chocolate, quantity = 10
Total cost without couponCode = $25.0
Calculating cost for Chocolate, quantity = 10
Total cost with couponCode = $22.5

Solution:

 final case class CartItem(name: String, quantity: Int, price: Double)


 object ShoppingCart {
   def totalCost(cartItem: CartItem, couponCode: Option[String]): Double = {
     println(s"Calculating cost for ${cartItem.name}, quantity = ${cartItem.quantity}")

 couponCode match {
   case Some(coupon) =>
     val discount = if (coupon == "COUPON_1234") 0.1 else 0.0
     val totalCost = cartItem.price * cartItem.quantity * (1 - discount)
     totalCost

   case None => cartItem.price * cartItem.quantity
     }
   }
 }


 val cartItem = CartItem("Chocolate", 10, 2.50)
 println(s"""Total cost without couponCode = $$${ShoppingCart.totalCost(cartItem, None)}""")
 println(s"""Total cost with couponCode = $$${ShoppingCart.totalCost(cartItem, Some("COUPON_1234"))}""")

You should see the following output when you run your Scala application in IntelliJ:


Calculating cost for Chocolate, quantity = 10
Total cost without couponCode = $25.0
Calculating cost for Chocolate, quantity = 10
Total cost with couponCode = $22.5

Problem 18: Write a Scala program and use a case class to represent a Vehicle type with a name property. Define a trait named VehicleInventory that will define the methods only - that is, without any implementations - for typical Create, Read, Update, and Delete (CRUD) methods nsmed create(), read() update(), and delete(). Each of the aforementioned methods will have as input parameter a Vehicle type, and will have no return type. Next, extend the VehicleInventory trait with a class named VehicleInventorySystem that will, of course, provide implementation details to the create(), read(), update(), and delete() methods from the VehicleInventory trait. Then, create an object, or instance, of the Vehicle type for a "bmw car 5 series", along with an object, or instance, of the VehicleInventorySystem, and pass-through the Vehicle object to the create(), read(), update(), and delete() methods of the VehicleInventorySystem object.
Output:
Create vehicle = Vehicle(bmw car 5 series)
Read vehicle = Vehicle(bmw car 5 series)
Update vehicle = Vehicle(bmw car 5 series)
Delete vehicle = Vehicle(bmw car 5 series)

Solution:

  case class Vehicle(name: String)

 trait VehicleInventory {
   def create(vehicle: Vehicle): Unit
   def read(vehicle: Vehicle): Unit
   def update(vehicle: Vehicle): Unit
   def delete(vehicle: Vehicle): Unit
 }

 class VehicleInvetorySystem extends VehicleInventory {
   override def create(vehicle: Vehicle): Unit = {
     println(s"Create vehicle = $vehicle")
 }

 override def read(vehicle: Vehicle): Unit = {
   println(s"Read vehicle = $vehicle")
 }

 override def update(vehicle: Vehicle): Unit = {
   println(s"Update vehicle = $vehicle")
 }

 override def delete(vehicle: Vehicle): Unit = {
   println(s"Delete vehicle = $vehicle")
  }
 }

 val carBmw5 = Vehicle("bmw car 5 series")

 val vehicleInventorySystem = new VehicleInvetorySystem()
 vehicleInventorySystem.create(carBmw5)
 vehicleInventorySystem.read(carBmw5)
 vehicleInventorySystem.update(carBmw5)
 vehicleInventorySystem.delete(carBmw5)

You should see the following output when you run your Scala application in IntelliJ:


Create vehicle = Vehicle(bmw car 5 series)
Read vehicle = Vehicle(bmw car 5 series)
Update vehicle = Vehicle(bmw car 5 series)
Delete vehicle = Vehicle(bmw car 5 series)

Problem 19: Write a Scala program and use an abstract class to define a Vehicle type with a name property. Next, create two case classes, named, Car, and Bike, to model a given Vehicle - using, of course, the Vehicle type. Create a trait named VehicleInventory that has a type parameter, V, and will define the methods only - that is, methods without implementations - to model a typical create(), read(), update(), and delete(), CRUD operations. Each of these CRUD methods will have an input parameter for a given vehicle, where the type is based from the trait's type parameter. Using the VehicleInventory trait, create a class named VehicleInventorySystem that implements all the CRUD methods of the base VehicleInventory trait. Next, create objects, or instances, for the following cars, and bikes:
- bmw car 5 series
- mazda car 3 series
- honda bike firestorm
- bmw bike r 2000
With the above objects created, create an object, or instance, of the VehicleInventorySystem, and pass-through the bmw car to the create() method, the mazda car to the read() method, the honda bike to the update() method, and the bmw bike to the delete method. The output for your Scala program should be as follows:
Output:
Create vehicle = Car(bmw car 5 series)
Read vehicle = Car(mazda car 3 series)
Update vehicle = Bike(honda bike firestorm)
Delete vehicle = Bike(bmw bike r 2000)

Solution:

 abstract class Vehicle(name: String)
 case class Car(name: String) extends Vehicle(name)
 case class Bike(name: String) extends Vehicle(name)

 trait VehicleInventory[V] {
   def create(vehicle: V): Unit
   def read(vehicle: V): Unit
   def update(vehicle: V): Unit
   def delete(vehicle: V): Unit
 }

 class VehicleInvetorySystem[V] extends VehicleInventory[V] {
   override def create(vehicle: V): Unit = {
     println(s"Create vehicle = $vehicle")
 }

 override def read(vehicle: V): Unit = {
   println(s"Read vehicle = $vehicle")
 }

 override def update(vehicle: V): Unit = {
   println(s"Update vehicle = $vehicle")
 }

 override def delete(vehicle: V): Unit = {
   println(s"Delete vehicle = $vehicle")
   }
 }

 val carBmw5 = Car("bmw car 5 series")
 val carMazda5 = Car("mazda car 3 series")
 val bikeHonda = Bike("honda bike firestorm")
 val bikeBmwR = Bike("bmw bike r 2000")

 val vehicleInventorySystem = new VehicleInvetorySystem[Vehicle]()
 vehicleInventorySystem.create(carBmw5)
 vehicleInventorySystem.read(carMazda5)
 vehicleInventorySystem.update(bikeHonda)
 vehicleInventorySystem.delete(bikeBmwR)

You should see the following output when you run your Scala application in IntelliJ:


Create vehicle = Car(bmw car 5 series)
Read vehicle = Car(mazda car 3 series)
Update vehicle = Bike(honda bike firestorm)
Delete vehicle = Bike(bmw bike r 2000)

Problem 20: Write a Scala program and use a trait to define a Vehicle type with an abstract method named printName() that has no return type. Using this new Vehicle type, create a new class named Car with a name property of type String that is a sub-class of the former Vehicle type. Likewise, use this new Car type to create two subsequent classes, namely, a BmwCar, and a MazdaCar class. In addition, create a new type named Bike with a name property of type String that is a sub-class of the Vehicle trait. Using this new Bike class, create two further sub-classes, namely, a HondaBike, and a BmwBike, class. Next, create a class named VehicleMaker which has a type parameter V that acts as a constraint to the Vehicle type, and therefore, also has a vehicle of type V as its constructor argument. The VehicleMaker class should have a make() method with a generic return type V - that is, of course, based from and relative to its class type parameter. In the body of the make() method, you can simply access and call the printName() method for a given Vehicle type. With the above defined and in scope, use a List data structure to create a "bmw car 5 series" and a "mazda car 3 series". Similarly, create another List data structure with a "honda bike firestorm" and a "bmw bike r 2000". Thereafter, merge the two List data structures into a single one and use Scala's built-in map() method to call the VehicleMaker's make() method for each vehicle in the List.
Output:
Making vehicle = bmw car 5 series
Making vehicle = mazda car 3 series
Making vehicle = honda bike firestorm
Making vehicle = bmw bike r 2000

Solution:

 trait Vehicle {
   def printName(): Unit
 }

 class Car(name: String) extends Vehicle {
   override def printName(): Unit = println(name)
 }

 class BmwCar(name: String) extends Car(name) {
   override def printName(): Unit = println(name)
 }

 class MazdaCar(name: String) extends Car(name) {
   override def printName(): Unit = println(name)
 }

 class Bike(name: String) extends Vehicle{
   override def printName(): Unit = println(name)
 }

 class HondaBike(name: String) extends Bike(name) {
   override def printName(): Unit = println(name)
 }

 class BmwBike(name: String) extends Bike(name) {
   override def printName(): Unit = println(name)
 }


 class VehicleMaker[V <: Vehicle](val vehicle: V) {
   def make(): V = {
     print("Making vehicle = ")
     vehicle.printName()
     vehicle
   }
 }


 val carsToMake = List(new BmwCar("bmw car 5 series"), new MazdaCar("mazda car 3 series"))
 val bikesToMake = List(new HondaBike("honda bike firestorm"), new BmwBike("bmw bike r 2000"))
 val vehiclesToMake = carsToMake ++ bikesToMake

 vehiclesToMake.map { v =>
   new VehicleMaker[Vehicle](v).make()
 }

You should see the following output when you run your Scala application in IntelliJ:


Making vehicle = bmw car 5 series
Making vehicle = mazda car 3 series
Making vehicle = honda bike firestorm
Making vehicle = bmw bike r 2000

Problem 21: Write a Scala program that will model a basic Vehicle class system, or type hierarchy, as per the previous Scala exercise. More precisely, you will have a base trait for a Vehicle type and with an abstract printName() method. The Vehicle type has two sub-classes, namely, a Car, and a Bike. The Car type itself has two further subsequent sub-types, namely, a BmwCar and a MazdaCar. Likewise, the Bike type has two sub-classes, namely, a HondaBike, and a BmwBike. Modify the VehicleMaker class from the previous Scala exercise, and make its constructor argument optional, and do remember that the VehicleMaker had a type parameter with a constraint to the Vehicle type. In the VehicleMaker class, create a method named makeSimilarCars that will have as input parameters two vehicles whose types are of course derived from the Vehicle type constraint. In addition, this particular makeSimilarCars() method, as its name implies, should only accept objects that are of the same types - for instance, you could pass-through two BmwCar objects, and not a BmwCar along with a HondaBike. In addition, create another method named makeBikes() that also has two input parameters for two Vehicle types. As its name implies, though, this particular method will accept objects, say, a HondaBike with a BmwBike. As a hint to creating the makeSimilarCars() and the makeBikes() methods, you will need to lift some types into scope and generalize type constraints for the arguments using Scala's built-in implicit evidence features.
Output:
Making two CAR vehicles:
vehicleA = bmw car 3 series
vehicleB = bmw car 5 series
Making two BIKE vehicles:
vehicleA = honda bike firestorm
vehicleB = bmw bike r 2000

Solution:

 trait Vehicle {
   def printName(): Unit
 }

 class Car(name: String) extends Vehicle {
   override def printName(): Unit = println(name)
 }

 class BmwCar(name: String) extends Car(name) {
   override def printName(): Unit = println(name)
 }

 class MazdaCar(name: String) extends Car(name) {
   override def printName(): Unit = println(name)
 }

 class Bike(name: String) extends Vehicle{
   override def printName(): Unit = println(name)
 }

 class HondaBike(name: String) extends Bike(name) {
   override def printName(): Unit = println(name)
 }

 class BmwBike(name: String) extends Bike(name) {
   override def printName(): Unit = println(name)
 }


class VehicleMaker[V <: Vehicle](val vehicle: Option[V] = None) {
 def make(): Option[V] = {
   print("Making vehicle = ")
   vehicle.map { v =>
   v.printName()
   v
   }
 }

 def makeSimilarCars[A <: V, B <: V](vehicleA: A, vehicleB: B)(implicit ev: A =:= B): Unit = {
   println("Making two CAR vehicles: ")
   print("vehicleA = ")
   vehicleA.printName()
   print("vehicleB = ")
   vehicleB.printName()
 }

 def makeBikes[A <: V, B <: V](vehicleA: A, vehicleB: B)(implicit ev: A <:< V): Unit = {
   println("Making two BIKE vehicles: ")
   print("vehicleA = ")
   vehicleA.printName()
   print("vehicleB = ")
   vehicleB.printName()
 }

 }


 val bmw5Car = new BmwCar("bmw car 5 series")
 val bmw3Car = new BmwCar("bmw car 3 series")
 val hondaBike = new HondaBike("honda bike firestorm")
 val bmwBike = new BmwBike("bmw bike r 2000")

// new VehicleMaker[Vehicle]().makeSimilarCars(bmwCar, hondaBike) //this will not compile
 new VehicleMaker[Vehicle]().makeSimilarCars(bmw3Car, bmw5Car)


// new VehicleMaker[Vehicle]().makeBikes(hondaBike, mazdaCar) // this will not compile
 new VehicleMaker[Vehicle]().makeBikes(hondaBike, bmwBike)

You should see the following output when you run your Scala application in IntelliJ:


Making two CAR vehicles: 
vehicleA = bmw car 3 series
vehicleB = bmw car 5 series
Making two BIKE vehicles: 
vehicleA = honda bike firestorm
vehicleB = bmw bike r 2000

Problem 22: Write a Scala program and use an abstract class to model a Vehicle type with a name property. Using the Vehicle type as the base class, create two case classes, namely, Bike and Car, to model a given bike or car object, respectively. Next, use a trait with type parameters with a constraint to sub-classes of the Vehicle type and name it trait VehicleDatabaseService. This particular trait will act as the underlying feature to interact with some underlying storage layer and, as such, it will have the following methods: addOrUpdate(), get(), and remove(). Each of the aforementioned method will have as input a Vehicle type and with no return type. They should, however, be only visible to sub-types of the VehicleDatabaseService. Each of the methods should further simply output some print statements of its respective operations as per the output below. Then, create another trait named VehicleInventory that also has a type parameter constraint to sub-types of the Vehicle type. It further defines the following methods: create(), read(), update(), delete(), and each of the methods have a Vehicle type as input parameter, and with no return type. These methods will, however, not provide any implementation details. Next, create a class named VehicleInventory with a type parameter constrained to sub-types of the Vehicle type, and adds the features of the VehicleInventory and the VehicleDatabaseService traits. Its create(), read(), update(), and delete() methods should merely wire in the corresponding methods of the VehicleDatabaseService, such as, addOrUpdate(), get(), and remove(). Finally, create an object, or instance, of the VehicleInventorSystem and call the create(), read(), update(), and delete() methods by passing through a "bmw 3 series" car, "mazda 3 series" car, a "honda bike firestorm", and a "bmw bike r 2000".
Output:
Create vehicle = Car(bmw 3 series)
Adding or updating vehicle = Car(bmw 3 series)
Read vehicle = Car(mazda 3 series)
Getting vehicle = Car(mazda 3 series)
Update vehicle = Bike(honda bike firestorm)
Adding or updating vehicle = Bike(honda bike firestorm)
Delete vehicle = Bike(bmw bike r 2000)
Removing vehicle = Bike(bmw bike r 2000)

Solution:

 abstract class Vehicle(name: String)
 case class Car(name: String) extends Vehicle(name)
 case class Bike(name: String) extends Vehicle(name)

 trait VehicleDatabaseService[V <: Vehicle] {
   protected def addOrUpdate(vehicle: V): Unit = {
     println(s"Adding or updating vehicle = $vehicle")
 }

 protected def get(vehicle: V): Unit = {
   println(s"Getting vehicle = $vehicle")
 }

 protected def remove(vehicle: V): Unit = {
   println(s"Removing vehicle = $vehicle")
  }
 }

 trait VehicleInventory[V <: Vehicle] {
   def create(vehicle: V): Unit
   def read(vehicle: V): Unit
   def update(vehicle: V): Unit
   def delete(vehicle: V): Unit
 }

 class VehicleInvetorySystem[V <: Vehicle]
   extends VehicleInventory[V]
     with VehicleDatabaseService[V] {

 override def create(vehicle: V): Unit = {
   println(s"Create vehicle = $vehicle")
   addOrUpdate(vehicle)
 }

 override def read(vehicle: V): Unit = {
   println(s"Read vehicle = $vehicle")
   get(vehicle)
 }

 override def update(vehicle: V): Unit = {
   println(s"Update vehicle = $vehicle")
   addOrUpdate(vehicle)
 }

 override def delete(vehicle: V): Unit = {
   println(s"Delete vehicle = $vehicle")
   remove(vehicle)
  }
 }

 val vehicleInventorySystem = new VehicleInvetorySystem[Vehicle]()
 vehicleInventorySystem.create(Car("bmw 3 series"))
 vehicleInventorySystem.read(Car("mazda 3 series"))
 vehicleInventorySystem.update(Bike("honda bike firestorm"))
 vehicleInventorySystem.delete(Bike("bmw bike r 2000"))

You should see the following output when you run your Scala application in IntelliJ:


Create vehicle = Car(bmw 3 series)
Adding or updating vehicle = Car(bmw 3 series)
Read vehicle = Car(mazda 3 series)
Getting vehicle = Car(mazda 3 series)
Update vehicle = Bike(honda bike firestorm)
Adding or updating vehicle = Bike(honda bike firestorm)
Delete vehicle = Bike(bmw bike r 2000)
Removing vehicle = Bike(bmw bike r 2000)

Problem 23:  In this Scala exercise, we will reuse most of the constructs from the previous exercise, and extend it to focus on one - out of the many - built-in patterns for dependency injection in Scala. Therefore, keep in mind the previous classes and traits from the earlier section - that is, abstract class Vehicle, case class Car, case class Bike, trait VehicleDatabaseService, trait VehicleInventory, and class VehicleInventorySystem. With the above-mentioned defined, you will need to create a new trait named VehicleSystem that has a type parameter for all sub-types of Vehicle. This new trait will also define a value of type VehicleInventorySystem, but leaving the right-hand-side object instantiation of the latter empty or undefined. You can think of this particular VehicleSystem trait acting as a business layer and with business friendly method names, such as, a checkVehicleStock() method with an input parameter of the same type as the trait's type parameter. The method will basically call the VehicleInventorySystem's read() method. Next, define an object, or instance, of the VehicleSystem for Vehicle types, and override its VehicleInventorySystem with an actual object, or instance, of VehicleInventorySystem of type Vehicle. Thereafter, call the checkVehicleStock() method for a given Car "mazda 3 series", and a Bike "Honda bike firestorm". The output should be as follows:
Output:
Checking vehicle stock for vehicle = Car(mazda 3 series)
Read vehicle = Car(mazda 3 series)
Getting vehicle = Car(mazda 3 series)
Checking vehicle stock for vehicle = Bike(honda bike firestorm)
Read vehicle = Bike(honda bike firestorm)
Getting vehicle = Bike(honda bike firestorm)

Solution:

 abstract class Vehicle(name: String)
 case class Car(name: String) extends Vehicle(name)
 case class Bike(name: String) extends Vehicle(name)

 trait VehicleDatabaseService[V <: Vehicle] {
   protected def addOrUpdate(vehicle: V): Unit = {
     println(s"Adding or updating vehicle = $vehicle")
 }

 protected def get(vehicle: V): Unit = {
   println(s"Getting vehicle = $vehicle")
 }

 protected def remove(vehicle: V): Unit = {
   println(s"Removing vehicle = $vehicle")
  }
}

 trait VehicleInventory[V <: Vehicle] {
   def create(vehicle: V): Unit
   def read(vehicle: V): Unit
   def update(vehicle: V): Unit
   def delete(vehicle: V): Unit
 }

 class VehicleInvetorySystem[V <: Vehicle]
   extends VehicleInventory[V]
     with VehicleDatabaseService[V] {

 override def create(vehicle: V): Unit = {
   println(s"Create vehicle = $vehicle")
   addOrUpdate(vehicle)
 }

 override def read(vehicle: V): Unit = {
   println(s"Read vehicle = $vehicle")
   get(vehicle)
 }

 override def update(vehicle: V): Unit = {
   println(s"Update vehicle = $vehicle")
   addOrUpdate(vehicle)
 }

 override def delete(vehicle: V): Unit = {
   println(s"Delete vehicle = $vehicle")
   remove(vehicle)
   }
 }


 // acts as a business layer
 trait VehicleSystem[V <: Vehicle] {
   val vehicleInventorySystem: VehicleInvetorySystem[V]

   def checkVehicleStock(vehicle: V): Unit = {
     println(s"Checking vehicle stock for vehicle = $vehicle")
     vehicleInventorySystem.read(vehicle)
   }
 }


 val vehicleSystem = new VehicleSystem[Vehicle] {
   override val vehicleInventorySystem: VehicleInvetorySystem[Vehicle] = new VehicleInvetorySystem[Vehicle]
 }

 vehicleSystem.checkVehicleStock(Car("mazda 3 series"))
 vehicleSystem.checkVehicleStock(Bike("honda bike firestorm"))

You should see the following output when you run your Scala application in IntelliJ:


Checking vehicle stock for vehicle = Car(mazda 3 series)
Read vehicle = Car(mazda 3 series)
Getting vehicle = Car(mazda 3 series)
Checking vehicle stock for vehicle = Bike(honda bike firestorm)
Read vehicle = Bike(honda bike firestorm)
Getting vehicle = Bike(honda bike firestorm)

Problem 24: This Scala exercise is yet another example of Scala's built-in support for dependency injection, and we will reuse some of the constructs from the earlier Scala exercises that you are already familiar with, such as, the abstract class Vehicle, the case class Car, and the case class Bike. Next, create a new class named VehicleInventorService that has a type parameter for all sub-types of Vehicle. It will define a checkStock() method with an input parameter of the same type as the class's type parameter. Then, create a VehiclePricingService class that also has a type parameter for all sub-types of Vehicle. It will define a checkPrice() method with an input parameter of the same type as the class's type parameter. Use these new above-mentioned classes, and create two lazy objects, or instances, in a new trait named VehicleServices with a type parameter for all sub-types of Vehicle. You can think of this VehicleServices trait as a layer for encapsulating all the given service layers for a particular vehicle system. Naturally, next, we define the VehicleSystem trait which also has a type parameter for all sub-types of Vehicle. It however uses the self-type approach in Scala, to narrow down the - this: - to the VehicleServices trait above. Of course, any instance of the VehicleSystem trait will have to, inject, or mixin, VehiclesServices. It further defines a buyVehicle() method that logically calls the checkStock() method from the VehicleInventoryService, and the checkPrice() of the VehiclePricingService, respectively. To use the above layers, create a singleton object named VehicleApp that will extend the VehicleSystem and inject, or mixin, the VehiclesServices trait, for the Vehicle type. Finally, call the buyVehicle() method of the VehicleApp for a given Car "mazda 3 series" , and a bike "honda bike firestorm".
Output:
buying vehicle Car(mazda 3 series)
checking stock for vehicle = Car(mazda 3 series)
checking price for vehicle = Car(mazda 3 series)
buying vehicle Bike(honda bike firestorm)
checking stock for vehicle = Bike(honda bike firestorm)
checking price for vehicle = Bike(honda bike firestorm)

Solution:

 abstract class Vehicle(name: String)
 case class Car(name: String) extends Vehicle(name)
 case class Bike(name: String) extends Vehicle(name)

 class VehicleInventoryService[V <: Vehicle] {
   def checkStock(vehicle: V): Unit = {
     println(s"checking stock for vehicle = $vehicle")
   }
 }

 class VehiclePricingService[V <: Vehicle] {
   def checkPrice(vehicle: V): Unit = {
     println(s"checking price for vehicle = $vehicle")
   }
 }

 trait VehicleServices[V <: Vehicle] {
   lazy val vehicleInventoryService = new VehicleInventoryService[V]
   lazy val vehiclePricingService = new VehiclePricingService[V]
 }

 trait VehicleSystem[V <: Vehicle] {
   this: VehicleServices[V] =>

 def buyVehicle(vehicle: V): Unit = {
   println(s"buying vehicle $vehicle")
   vehicleInventoryService.checkStock(vehicle)
   vehiclePricingService.checkPrice(vehicle)
   }
 }


 object VehicleApp extends VehicleSystem[Vehicle] with VehicleServices[Vehicle]

 VehicleApp.buyVehicle(Car("mazda 3 series"))
 VehicleApp.buyVehicle(Bike("honda bike firestorm"))

You should see the following output when you run your Scala application in IntelliJ:


buying vehicle Car(mazda 3 series)
checking stock for vehicle = Car(mazda 3 series)
checking price for vehicle = Car(mazda 3 series)
buying vehicle Bike(honda bike firestorm)
checking stock for vehicle = Bike(honda bike firestorm)
checking price for vehicle = Bike(honda bike firestorm)

 

Nadim Bahadoor on FacebookNadim Bahadoor on GithubNadim Bahadoor on LinkedinNadim Bahadoor on Twitter
Nadim Bahadoor
Technology and Finance Consultant with over 14 years of hands-on experience building large scale systems in the Financial (Electronic Trading Platforms), Risk, Insurance and Life Science sectors. I am self-driven and passionate about Finance, Distributed Systems, Functional Programming, Big Data, Semantic Data (Graph) and Machine Learning.