8 minutes read

Often, when we write a large program, we have to define our models — special data types that are not simple types like a number, a string, or a boolean variable. For example, we can define a CreditCard data class. In the program, we often need to write such classes, which then have to be created, compared, and displayed. It would be inconvenient to write a huge number of similar methods for each class, so Scala implements a special type of class called case class. Let's see what it is and how it makes a developer's life easier!

Defining case classes

First, let's see how to define a case class:

case class CreditCard(number: String, balance: Int)

We use the case class specifier and list the constituent fields, but note that, unlike regular classes, we don't use the val specifier. This is because case classes have fields that are immutable by default.

Now let's look at the initialization. To create an instance of a case class we can write:

val myCreditCard = CreditCard("3546-1234-4635-4998", 255)

Nothing special, but in Scala 2 we don't have to add the new keyword. By default, case classes have the apply method that accepts class fields and initializes the instance.

Of course, we can also define our own class methods to implement some kind of logic:

case class CreditCard(number: String, balance: Int):
  def encryptedCardNumber = s"****-****-****-${number.takeRight(4)}"

Often such methods have relatively simple logic, for example, converting some fields as shown above.

Special methods

Let's see what other advantages case classes have over regular classes. First, we can compare case class instances, and they will be compared by each field inside them! The following code will print true to the console:

val myCreditCard = CreditCard("3546-1234-4635-4998", 255)
val anotherCard  = CreditCard("3546-1234-4635-4998", 255)

println(myCard == anotherCard) // true

Class fields can also be other classes, for example:

case class Person(name: String, card: CreditCard)

In this case, the credit cards will also be compared when comparing two persons.

Another useful feature that case classes have is neat printing. Let's create an instance of the case class above:

val person = Person("Bob", CreditCard("3546-1234-4635-4998", 255))

Printing the person to the console:

scala> println(person)
Person(Bob,CreditCard(3546-1234-4635-4998,255))

This is a very useful feature that sometimes helps debug the code.

If we want to create a duplicate of the data, but with the fields changed, we can use the copy method. All parameters of the method correspond to the fields of the class and by default take the values from the instance. As a result, we can get an instance with the same number and an updated balance:

myCreditCard.copy(balance = myCreditCard.balance + 1000)
// CreditCard("3546-1234-4635-4998", 1255)

Also, case classes, unlike ordinary classes, have the hashCode and equals methods that are used in more specific cases.

Pattern matching

We can easily match our case classes:

person match
  case Person(name, creditCard) => creditCard.encryptedCardNumber

Here we call the encryptedCardNumber function because we know that the second parameter of Person is CreditCard.

As you can see in the example, we don't use the name of our person in any way. In this case, to avoid cluttering up the code with unnecessary variables, we can skip them with an underscore. Also, we can match the internal case class if we need its fields:

person match
  case Person(_, CreditCard(_, balance)) => s"My balance is $balance"

Inheritance

Case classes can inherit from trait, class, or abstract class, but they cannot inherit from other case classes!

trait Device:
  def id: String

case class Computer(cpu: String, id: String) extends Device
case class TV(matrix: String, id: String)    extends Device
case class Phone(model: String, id: String)  extends Device

Similarly to regular classes, we can override fields. In this example, the override keyword can be omitted because the id has no implementation.

Conclusion

To sum up, in this topic you got acquainted with case classes in Scala! You learned that they are very similar to regular classes but have a number of great advantages, such as neat printing, comparison, pattern matching, and much more.

3 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo