Skip to content

Commit c5c3a1f

Browse files
committed
Rewrote case classes tour
1 parent 6b9bd6e commit c5c3a1f

File tree

1 file changed

+29
-115
lines changed

1 file changed

+29
-115
lines changed

tutorials/tour/_posts/2017-02-13-case-classes.md

Lines changed: 29 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -9,135 +9,49 @@ categories: tour
99
num: 11
1010
next-page: pattern-matching
1111
previous-page: currying
12+
prerequisite-knowledge: classes, basics, mutability
1213
---
1314

14-
Scala supports the notion of _case classes_. Case classes are just regular classes that are:
15-
16-
* Immutable by default
17-
* Decomposable through [pattern matching](pattern-matching.html)
18-
* Compared by structural equality instead of by reference
19-
* Succinct to instantiate and operate on
20-
21-
Here is an example for a Notification type hierarchy which consists of an abstract super class `Notification` and three concrete Notification types implemented with case classes `Email`, `SMS`, and `VoiceRecording`.
15+
Case classes are like regular classes with a few key differences which we will go over. Case classes are good for modeling immutable data. In the next step of the tour, we'll see how they are useful in [pattern matching](pattern-matching.html).
2216

17+
## Defining a case class
18+
A minimal case class requires the keywords `case class`, an identifier, and a parameter list (which may be empty):
2319
```tut
24-
abstract class Notification
25-
case class Email(sourceEmail: String, title: String, body: String) extends Notification
26-
case class SMS(sourceNumber: String, message: String) extends Notification
27-
case class VoiceRecording(contactName: String, link: String) extends Notification
28-
```
20+
case class Souvenir()
2921
30-
Instantiating a case class is easy: (Note that we don't need to use the `new` keyword)
31-
32-
```tut
33-
val emailFromJohn = Email("john.doe@mail.com", "Greetings From John!", "Hello World!")
22+
val souvenir = Souvenir()
3423
```
24+
Notice how the keyword `new` was not used to instantiate the `Message` case class. This is because case classes have an `apply` method by default which takes care of object construction.
3525

36-
The constructor parameters of case classes are treated as public values and can be accessed directly.
37-
38-
```tut
39-
val title = emailFromJohn.title
40-
println(title) // prints "Greetings From John!"
26+
When you create a case class with parameters, the parameters are public `val`s.
4127
```
28+
case class Message(sender: String, recipient: String, body: String)
29+
val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?")
4230
43-
With case classes, you cannot mutate their fields directly. (unless you insert `var` before a field, but doing so is generally discouraged).
44-
45-
```tut:fail
46-
emailFromJohn.title = "Goodbye From John!" // This is a compilation error. We cannot assign another value to val fields, which all case classes fields are by default.
31+
println(message1.sender) // prints guillaume@quebec.ca
32+
message1.sender = "travis@washington.us" // this line does not compile
4733
```
34+
You can't reassign `message1.sender` because it is a `val` (i.e. immutable). It is possible to use `var`s in case classes but this is discouraged.
4835

49-
Instead, you make a copy using the `copy` method. As seen below, you can replace just some of the fields:
50-
51-
```tut
52-
val editedEmail = emailFromJohn.copy(title = "I am learning Scala!", body = "It's so cool!")
53-
54-
println(emailFromJohn) // prints "Email(john.doe@mail.com,Greetings From John!,Hello World!)"
55-
println(editedEmail) // prints "Email(john.doe@mail.com,I am learning Scala,It's so cool!)"
36+
## Comparison
37+
Case classes are compared by structure and not by reference:
5638
```
39+
case class Message(sender: String, recipient: String, body: String)
5740
58-
For every case class the Scala compiler generates an `equals` method which implements structural equality and a `toString` method. For instance:
59-
60-
```tut
61-
val firstSms = SMS("12345", "Hello!")
62-
val secondSms = SMS("12345", "Hello!")
63-
64-
if (firstSms == secondSms) {
65-
println("They are equal!")
66-
}
67-
68-
println("SMS is: " + firstSms)
41+
val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
42+
val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
43+
val messagesAreTheSame = message2 == message3 // true
6944
```
45+
Even though `message2` and `message3` refer to different objects, the value of each object is equal.
7046

71-
will print
72-
47+
## Copying
48+
You can create a deep copy of an instance of a case class simply by using the `copy` method. You can optionally change the constructor arguments.
7349
```
74-
They are equal!
75-
SMS is: SMS(12345, Hello!)
50+
case class Message(sender: String, recipient: String, body: String)
51+
val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg")
52+
val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr")
53+
message5.sender // travis@washington.us
54+
message5.recipient // claire@bourgogne.fr
55+
message5.body // "Me zo o komz gant ma amezeg"
7656
```
77-
78-
With case classes, you can utilize **pattern matching** to work with your data. Here's a function that prints out different messages depending on what type of Notification is received:
79-
80-
```tut
81-
def showNotification(notification: Notification): String = {
82-
notification match {
83-
case Email(email, title, _) =>
84-
"You got an email from " + email + " with title: " + title
85-
case SMS(number, message) =>
86-
"You got an SMS from " + number + "! Message: " + message
87-
case VoiceRecording(name, link) =>
88-
"you received a Voice Recording from " + name + "! Click the link to hear it: " + link
89-
}
90-
}
91-
92-
val someSms = SMS("12345", "Are you there?")
93-
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
94-
95-
println(showNotification(someSms))
96-
println(showNotification(someVoiceRecording))
97-
98-
// prints:
99-
// You got an SMS from 12345! Message: Are you there?
100-
// you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
101-
```
102-
103-
Here's a more involved example using `if` guards. With the `if` guard, the pattern match branch will fail if the condition in the guard returns false.
104-
105-
```tut
106-
def showNotificationSpecial(notification: Notification, specialEmail: String, specialNumber: String): String = {
107-
notification match {
108-
case Email(email, _, _) if email == specialEmail =>
109-
"You got an email from special someone!"
110-
case SMS(number, _) if number == specialNumber =>
111-
"You got an SMS from special someone!"
112-
case other =>
113-
showNotification(other) // nothing special, delegate to our original showNotification function
114-
}
115-
}
116-
117-
val SPECIAL_NUMBER = "55555"
118-
val SPECIAL_EMAIL = "jane@mail.com"
119-
val someSms = SMS("12345", "Are you there?")
120-
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
121-
val specialEmail = Email("jane@mail.com", "Drinks tonight?", "I'm free after 5!")
122-
val specialSms = SMS("55555", "I'm here! Where are you?")
123-
124-
println(showNotificationSpecial(someSms, SPECIAL_EMAIL, SPECIAL_NUMBER))
125-
println(showNotificationSpecial(someVoiceRecording, SPECIAL_EMAIL, SPECIAL_NUMBER))
126-
println(showNotificationSpecial(specialEmail, SPECIAL_EMAIL, SPECIAL_NUMBER))
127-
println(showNotificationSpecial(specialSms, SPECIAL_EMAIL, SPECIAL_NUMBER))
128-
129-
// prints:
130-
// You got an SMS from 12345! Message: Are you there?
131-
// you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
132-
// You got an email from special someone!
133-
// You got an SMS from special someone!
134-
135-
```
136-
137-
When programming in Scala, it is recommended that you use case classes pervasively to model/group data as they help you to write more expressive and maintainable code:
138-
139-
* Immutability frees you from needing to keep track of where and when things are mutated
140-
* Comparison-by-value allows you compare instances as if they are primitive values - no more uncertainty regarding whether instances of a class is compared by value or reference
141-
* Pattern matching simplifies branching logic, which leads to less bugs and more readable code.
142-
143-
57+
The recipient of `message4` is used as the sender of `message5` but the `body` of `message4` was copied directly.

0 commit comments

Comments
 (0)