Case classes are a special kind of class created using the keyword case. When the Scala compiler sees a case class, it automatically generates boilerplate code so you don’t have to do it. Here’s an example of a Person class:
In this code example, you’re creating a Person case class with firstName and lastName parameters. But when you prefix a class with case, the following things will happen automatically:
Now think about how many times you’ve created a data transfer object (DTO) with only accessors for the purpose of wrapping some data. Scala’s case classes will make that easier for you the next time. Both equals and hashCodeimplementations also make it safer to use with collections.
Like any other class, a case class can extend other classes, including trait and case classes. When you declare an abstract case class, Scala won’t generate the apply method in the companion object. That makes sense because you can’t create an instance of an abstract class. You can also create case objects that are singleton and serializable:
Let’s put your recently gained knowledge of case classes to use in the MongoDB driver. So far, you’ve implemented basic find methods in your driver. It’s great, but you could do one more thing to the driver to make it more useful. MongoDB supports multiple query options like Sort, Skip, and Limit that you don’t support in your driver. Using case classes and a little pattern matching, you could do this easily. You’ll add a new finder method to the collection to find by query and query options. But first, let’s define the query options you’re going to support:
For the Query class, you’ll wrap your good old friend DBObject and expose methods like sort, skip, and limit so that users can specify query options:
Look how you extracted the first and last names from the object using pattern matching. The case clause should be familiar to you; here you’re using a variable pattern in which the matching values get assigned to the first and lastvariables. Under the hood, Scala handles this pattern matching using a method called unapply. If you have to handcode the companion object that gets generated for Person, it will look like following:
In the discussion of for-comprehensions in chapter 2, I didn’t mention that the generator part of for-comprehensions uses pattern matching. I can best describe this with an example. Here you’re creating a list of persons and looping through them using pattern matching:
You’ll see more examples of extractors and pattern matching throughout the book. Before we leave this section, I still owe you the implementation of the find-by-query method, so here you go (see the following listing).
- Listing 3.10 ReadOnly trait
When it comes to overload methods (methods with the same name), you have to specify the return type; otherwise, the code won’t compile. You have a similar limitation for recursive method calls. Scala type inference can’t infer the type of recursive methods or functions. In case of type errors, it’s always helpful to add type information. Using the test client in the following listing, you could test your new finder method.
- Listing 3.11 TestFindByQuery.scala
* Gossip@DesignPattern : Behavioral - Visitor 模式
* Scala Gossic : 繼續深入 - 模式比對 (案例類別)