Skip to content

Commit 4c3b321

Browse files
committed
Add ClassesVsCaseClasses and LazyEvaluation sections
1 parent f25cf97 commit 4c3b321

File tree

6 files changed

+530
-0
lines changed

6 files changed

+530
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scalatutorial.aux
2+
3+
case class Note(name: String, duration: String, octave: Int)
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,175 @@
11
package scalatutorial.sections
22

3+
import scalatutorial.aux.{BankAccount, Note}
4+
35
/** @param name classes_vs_case_classes */
46
object ClassesVsCaseClasses extends ScalaTutorialSection {
57

8+
/**
9+
* In the previous sections we have seen how case classes could be
10+
* used to achieve information aggregation, and also how classes
11+
* could be used to achieve data abstraction or to define stateful
12+
* objects.
13+
*
14+
* What are the relationship between classes and case classes? How
15+
* do they differ?
16+
*
17+
* = Creation and Manipulation =
18+
*
19+
* Remember the class definition of `BankAccount`:
20+
*
21+
* {{{
22+
* class BankAccount {
23+
*
24+
* private var balance = 0
25+
*
26+
* def deposit(amount: Int): Unit = {
27+
* if (amount > 0) balance = balance + amount
28+
* }
29+
*
30+
* def withdraw(amount: Int): Int =
31+
* if (0 < amount && amount <= balance) {
32+
* balance = balance - amount
33+
* balance
34+
* } else throw new Error("insufficient funds")
35+
* }
36+
* }}}
37+
*
38+
* And the case class definition of `Note`:
39+
*
40+
* {{{
41+
* case class Note(name: String, duration: String, octave: Int)
42+
* }}}
43+
*
44+
* Let’s create some instances of `BankAccount` and `Note` and manipulate them:
45+
*/
46+
def creationAndManipulation(res0: String): Unit = {
47+
val aliceAccount = new BankAccount
48+
val c3 = Note("C", "Quarter", 3)
49+
50+
c3.name shouldBe res0
51+
}
52+
/**
53+
* We see that creating a class instance requires the keyword `new`, whereas
54+
* this is not required for case classes.
55+
*
56+
* Also, we see that the case class constructor parameters are promoted to
57+
* members, whereas this is not the case with regular classes.
58+
*
59+
* = Equality =
60+
*
61+
*/
62+
def equality(res0: Boolean, res1: Boolean): Unit = {
63+
val aliceAccount = new BankAccount
64+
val bobAccount = new BankAccount
65+
66+
aliceAccount == bobAccount shouldBe res0
67+
68+
val c3 = Note("C", "Quarter", 3)
69+
val cThree = Note("C", "Quarter", 3)
70+
71+
c3 == cThree shouldBe res1
72+
}
73+
74+
/**
75+
* In the above example, the same definitions of bank accounts lead to different
76+
* values, whereas the same definitions of notes lead to equal values.
77+
*
78+
* As we have seen in the previous sections, stateful classes introduce a notion of ''identity''
79+
* that does not exist in case classes. Indeed, the value of `BankAccount` can change over
80+
* time whereas the value of a `Note` is immutable.
81+
*
82+
* In Scala, by default, comparing objects will compare their identity, but in the
83+
* case of case class instances, the equality is redefined to compare the values of
84+
* the aggregated information.
85+
*
86+
* = Pattern Matching =
87+
*
88+
* We saw how pattern matching can be used to extract information from a case class instance:
89+
*
90+
* {{{
91+
* c3 match {
92+
* case Note(name, duration, octave) => s"The duration of c3 is $duration"
93+
* }
94+
* }}}
95+
*
96+
* By default, pattern matching does not work with regular classes.
97+
*
98+
* = Extensibility =
99+
*
100+
* A class can extend another class, whereas a case class can not extend
101+
* another case class (because it would not be possible to correctly
102+
* implement their equality).
103+
*
104+
* = Case Classes Encoding =
105+
*
106+
* We saw the main differences between classes and case classes.
107+
*
108+
* It turns out that case classes are just a special case of classes,
109+
* whose purpose is to aggregate several values into a single value.
110+
*
111+
* The Scala language provides explicit support for this use case
112+
* because it is very common in practice.
113+
*
114+
* So, when we define a case class, the Scala compiler defines a class
115+
* enhanced with some more methods and a companion object.
116+
*
117+
* For instance, the following case class definition:
118+
*
119+
* {{{
120+
* case class Note(name: String, duration: String, octave: Int)
121+
* }}}
122+
*
123+
* Expands to the following class definition:
124+
*
125+
* {{{
126+
* class Note(_name: String, _duration: String, _octave: Int) extends Serializable {
127+
*
128+
* // Constructor parameters are promoted to members
129+
* val name = _name
130+
* val duration = _duration
131+
* val octave = _octave
132+
*
133+
* // Equality redefinition
134+
* override def equals(other: Any): Boolean = other match {
135+
* case that: Note =>
136+
* (that canEqual this) &&
137+
* name == that.name &&
138+
* duration == that.duration &&
139+
* octave == that.octave
140+
* case _ => false
141+
* }
142+
*
143+
* def canEqual(other: Any): Boolean = other.isInstanceOf[Note]
144+
*
145+
* // Java hashCode redefinition according to equality
146+
* override def hashCode(): Int = {
147+
* val state = Seq(name, duration, octave)
148+
* state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
149+
* }
150+
*
151+
* // toString redefinition to return the value of an instance instead of its memory addres
152+
* override def toString = s"Note($name, $duration, $octave)"
153+
*
154+
* // Create a copy of a case class, with potentially modified field values
155+
* def copy(name: String = name, duration: String = duration, octave: Int = octave): Note =
156+
* new Note(name, duration, octave)
157+
*
158+
* }
159+
*
160+
* object Note {
161+
*
162+
* // Constructor that allows the omission of the `new` keyword
163+
* def apply(name: String, duration: String, octave: Int): Note =
164+
* new Note(name, duration, octave)
165+
*
166+
* // Extractor for pattern matching
167+
* def unapply(note: Note): Option[(String, String, Int)) =
168+
* if (note eq null) None
169+
* else Some((note.name, note.duration, note.octave))
170+
*
171+
* }
172+
* }}}
173+
*/
174+
def nothing(): Unit = ()
6175
}

0 commit comments

Comments
 (0)