Blog

Scala Notes: Case Classes and Case Objects

Note: Throughout my career, I've kept field notes on programming languages, tools, and frameworks I've used. Whenever I have a cohesive set of notes, I'll edit them and publish them on my blog. This blog is the first of it's kind..

Case Classes

The first time I used a case class was to serialize a JSON string into a Scala object.

Sometime later, I used it in a Spark application:

Sometime after that, I used it in a Flink application:

In Scala, a case class has all the functionality of a class, but by using the case keyword, the compiler generates some code for you that enables some handy features. I'll go into some detail about these features and provide some examples below.

Immutable 

By default, Case Classes are immutable. Immutability is part of the zen of Scala, so this may not be too surprising. By default, case classes constructor parameters use the val keyword making them immutable. You will run into this if you ever create a case class and try to mutate one of its parameters.

Apply 

Since case classes produce a companion object with an apply method, you can also use an apply method to create an instance of a case class. 

Unapply

Case classes have an unapply method. Unapply allows you to get the parameters back from a case class. Unapply is useful for pattern matching and partial functions. 

Copy

Case classes have a copy method. Copies of case classes result in strict equivalence.

Since case classes are immutable, the copy method is also how you can "overwrite" parameters of a case class by creating a new instance of one. 

Tupled 

Case Classes provide an optional tuple constructor. I haven't ever had to use it, but I supposed having more ways to create a case class is a good thing.

Curried

Case Classes also provide an optional curried constructor. I've used this in a project and got asked a lot of questions about it.  

Other Stuff

Case classes have equals, hashCode, and toString methods. The first two ( equals and hashCode) are useful for determining equality as well as using in collections like Maps and Sets. The toString method helps when printing Case Classes to see an informative string rather than an object reference. 

Algebraic Data Types

Algebraic Data Types (ADTs) are types that are composed of other types. You can utilize the mechanics of case classes to construct ADTs. ADTs can help simplify some of the overhead of handling complex types with intersections. 

There are a lot of great post on ADTs on the web, I've linked a few below if you're more interested in the topic. 

  1. Algebraic Data Types in four languages

  2. Functional Design: Algebraic Data Types

  3. Algebraic Data Types

Project.jpg

What about case objects? 

Case objects are similar to Scala objects with a few critical differences. The main differences are that case objects are serializable and have a default hashCode implementation as well as a toString implementation. Case objects are useful for enumerations and creating containers for messages. 


Enumerations

Enumerations provide a convenient way to manage variations on a class. When constructing a complex object, they can help to ensure saftey across the combination of values.

Project%282%29.jpg

Message Containers 

As I mentioned earlier, pattern matching is an essential aspect of using case classes and objects, and using them as message containers allow for convenient pattern matching using objects without arguments. Akka code handling Bluetooth headphones might look something like this, for example:

Conclusion 

Case Classes and Case Objects provide a robust and convenient way of representing data in Scala. They can be represent data from a simple object with no parameters to a complex ADT.


ScalaJowanza JosephScala