Case classes
In Scala, a class can be defined with keyword case, and is called case class.
case class Player(name: String, sport: String, club: String)
So how is a case class different from a non-case class?
First, Scala compiler adds a factory method with same name as class. With this factory method, you can create instances of case class without using new keyword.
var p = Player("Messi","Football","Barcelona")
Second, all arguments in parameter list of a case class are by default val, and hence maintained as fields.
Third, Scala compiler also adds natural implementations for methods toString,equals and hash. As '==' in Scala invokes equals, so instances of case classes are compared structurally.
Compiler also adds a copy method to the case class, which enables users to make modified copies of an instance. We provide modified values for named parameters, and new instance is created with these new parameter values and values from copied instance for unspecified parameters.
val r = m.copy(name = "Ronaldo", club = "Real Madrid")
Apart from these convenient methods, biggest advantage of case classes is that they support pattern matching.
Pattern matching is a mechanism by which a value is checked against a pattern.
A match expression consists of a value, match keyword and a list of case clauses.
def checkValue(x: Int) = x match {
case 0 => "Nothing" case 1 => "Just one" case 2 => "Just two" case _ => "Many"}
Here, patterns are all literals. Such patterns are called Constant patterns. A constant pattern matches to itself only.
Match expression has a type, like this one has type String as all case clauses return String.
Case '_' represents catch all case, meaning it matches any value which doesn't match to other case clauses in the list.
Case classes are easy to use in pattern matching.
abstract class Shapecase class Triangle(base: Int, height: Int) extends Shape
case class RectAngle(width: Int, height: Int) extends Shape
case class Circle(radius: Int) extends Shape
case class AnyOtherShape(name: String) extends Shape
def getShapeArea(shape: Shape): Any = {
shape match {
case Triangle(base, height) => (base * height) / 2 case RectAngle(width, height) => width * height
case Circle(radius) => (Math.PI) * radius * radius
case _ => "Can't calculate" }
}
Above is an example of Constructor patterns.
Pattern matching can be done on type too. It may be helpful if a method is to be called on a pattern.
abstract class AutomobileThis is called Typed pattern.
case class Car(brand: String, buildYear: Int) extends Automobile{
def getCarDescription() = s"This is a $brand, built in year $buildYear"}
case class Crane(height: Int, capacity: Int) extends Automobile{
def getCraneDescription() = s"This is a $height meters high, $capacity tons capacity crane"}
def getAutomobileDescription(automobile: Automobile): Any = {
automobile match {
case car:Car => car.getCarDescription()
case crane:Crane => crane.getCraneDescription()
case _ => "No description available" }
}
println(getAutomobileDescription(Car("BMW", 2005)))
println(getAutomobileDescription(Crane(30, 1500)))
Similarly we have Sequence patterns(using Sequences), Tupled patterns(using tuples), and so on.