Skip to content

Commit e00efc6

Browse files
committed
Scala 2 Book migration - Scala Classes
1 parent dbe095e commit e00efc6

File tree

3 files changed

+241
-10
lines changed

3 files changed

+241
-10
lines changed

_overviews/scala3-book/domain-modeling-oop.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,61 @@ trait ComposedService extends GreetingService, TranslationService
173173
Abstract members in one trait (such as `translate` in `GreetingService`) are automatically matched with concrete members in another trait.
174174
This not only works with methods as in this example, but also with all the other abstract members mentioned above (that is, types, value definitions, etc.).
175175

176+
### Mixing traits in on the fly
177+
178+
Traits that have concrete methods can be mixed into classes on the fly. Given a class:
179+
180+
{% tabs traits_6 %}
181+
{% tab 'Scala 2 and 3' %}
182+
```scala
183+
class MyService(name: String)
184+
```
185+
{% endtab %}
186+
{% endtabs %}
187+
188+
you can create a `MyService` instance that mixes in the traits when you create it:
189+
190+
{% tabs traits_7 %}
191+
{% tab 'Scala 2 and 3' %}
192+
```scala
193+
val s = new MyService("ComposedService") with GreetingService with TranslationService
194+
// --------------------------------------------
195+
```
196+
{% endtab %}
197+
{% endtabs %}
198+
199+
The REPL shows that it works:
200+
201+
{% tabs traits_8 class=tabs-scala-version %}
202+
{% tab 'Scala 2' %}
203+
```scala
204+
scala> val s = new MyService("ComposedService") with GreetingService with TranslationService
205+
|
206+
val s: MyService with GreetingService with TranslationService = $anon$1@4ebd8d2
207+
208+
scala> s.translate("Text")
209+
val res0: String = ...
210+
211+
scala> s.sayHello
212+
val res1: String = ...
213+
```
214+
{% endtab %}
215+
{% tab 'Scala 3' %}
216+
```scala
217+
scala> val s = new MyService("ComposedService") with GreetingService with TranslationService
218+
val s: MyService & GreetingService & TranslationService = anon$1@c5a2d5
219+
220+
scala> s.translate("Text")
221+
val res0: String = ...
222+
223+
scala> s.sayHello
224+
val res1: String = ...
225+
```
226+
{% endtab %}
227+
{% endtabs %}
228+
229+
This example works because all the mixed in methods are defined in `GreetingService` and in `TranslationService`.
230+
176231
## Classes
177232

178233
Traits are great to modularize components and describe interfaces (required and provided).

_overviews/scala3-book/domain-modeling-tools.md

Lines changed: 179 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,26 +97,100 @@ p.vocation = "Musician"
9797
{% endtab %}
9898
{% endtabs %}
9999

100-
### Fields and methods
100+
### `val` makes fields read-only
101101

102-
Classes can also have methods and additional fields that are not part of constructors.
103-
They are defined in the body of the class.
104-
The body is initialized as part of the default constructor:
102+
In that first example both fields were defined as `var` fields:
103+
104+
{% tabs class_val_fields_1 %}
105+
{% tab 'Scala 2 and 3' %}
106+
```scala
107+
class Person(var name: String, var vocation: String)
108+
```
109+
{% endtab %}
110+
{% endtabs %}
111+
112+
That makes those fields mutable. You can also define them as `val` fields, which makes them immutable:
113+
114+
{% tabs class_val_fields_2 %}
115+
{% tab 'Scala 2 and 3' %}
116+
```scala
117+
class Person(val name: String, val vocation: String)
118+
// --- ---
119+
```
120+
{% endtab %}
121+
{% endtabs %}
122+
123+
If you now try to change the name of a `Person` instance, you’ll see an error:
124+
125+
{% tabs class_val_fields_3 class=tabs-scala-version %}
126+
{% tab 'Scala 2' %}
127+
```scala
128+
scala> class Person(val name: String, val vocation: String)
129+
class Person
130+
131+
scala> val p = new Person("Robert Allen Zimmerman", "Harmonica Player")
132+
val p: Person = Person@1c58d7be
133+
134+
scala> p.name = "Bob Dylan"
135+
^
136+
error: reassignment to val
137+
```
138+
{% endtab %}
139+
{% tab 'Scala 3' %}
140+
```scala
141+
scala> class Person(val name: String, val vocation: String)
142+
// defined class Person
143+
144+
scala> val p = Person("Robert Allen Zimmerman", "Harmonica Player")
145+
val p: Person = Person@779228dc
146+
147+
scala> p.name = "Bob Dylan"
148+
-- [E052] Type Error: ----------------------------------------------------------
149+
1 |p.name = "Bob Dylan"
150+
|^^^^^^^^^^^^^^^^^^^^
151+
|Reassignment to val name
152+
|
153+
| longer explanation available when compiling with `-explain`
154+
1 error found
155+
```
156+
{% endtab %}
157+
{% endtabs %}
158+
159+
### Class constructors
160+
161+
In Scala, the primary constructor of a class is a combination of:
162+
163+
- The constructor parameters
164+
- Methods that are called in the body of the class
165+
- Statements and expressions that are executed in the body of the class
166+
167+
Defining parameters in the primary constructor automatically creates fields in the class, and fields declared in the body
168+
of a Scala class are handled in a manner similar to Java; they are assigned when the class is first instantiated.
169+
170+
This `Person` class demonstrates several of the things you can do inside the body of a class:
105171

106172
{% tabs method class=tabs-scala-version %}
107173
{% tab 'Scala 2' %}
108174

109175
```scala
110176
class Person(var firstName: String, var lastName: String) {
111-
112177
println("initialization begins")
178+
// 'public' access by default
113179
val fullName = firstName + " " + lastName
114180

181+
// a private field
182+
private val HOME = System.getProperty("user.home")
183+
115184
// a class method
116185
def printFullName: Unit =
117186
// access the `fullName` field, which is created above
118187
println(fullName)
119188

189+
def printHome: Unit = println(s"HOME = $HOME")
190+
191+
// an overridden method
192+
override def toString(): String = fullName
193+
120194
printFullName
121195
println("initialization ends")
122196
}
@@ -128,15 +202,23 @@ class Person(var firstName: String, var lastName: String) {
128202

129203
```scala
130204
class Person(var firstName: String, var lastName: String):
131-
132205
println("initialization begins")
206+
// 'public' access by default
133207
val fullName = firstName + " " + lastName
134208

209+
// a private field
210+
private val HOME = System.getProperty("user.home")
211+
135212
// a class method
136213
def printFullName: Unit =
137214
// access the `fullName` field, which is created above
138215
println(fullName)
139216

217+
def printHome: Unit = println(s"HOME = $HOME")
218+
219+
// an overridden method
220+
override def toString(): String = fullName
221+
140222
printFullName
141223
println("initialization ends")
142224
```
@@ -153,10 +235,16 @@ scala> val john = new Person("John", "Doe")
153235
initialization begins
154236
John Doe
155237
initialization ends
156-
val john: Person = Person@55d8f6bb
238+
val john: Person = John Doe
157239

158240
scala> john.printFullName
159241
John Doe
242+
243+
scala> john.printHome
244+
HOME = /Users/al
245+
246+
scala> println(john)
247+
John Doe
160248
````
161249
{% endtab %}
162250
{% tab 'Scala 3' %}
@@ -165,10 +253,16 @@ scala> val john = Person("John", "Doe")
165253
initialization begins
166254
John Doe
167255
initialization ends
168-
val john: Person = Person@55d8f6bb
256+
val john: Person = John Doe
169257

170258
scala> john.printFullName
171259
John Doe
260+
261+
scala> john.printHome
262+
HOME = /Users/al
263+
264+
scala> println(john)
265+
John Doe
172266
````
173267
{% endtab %}
174268
{% endtabs %}
@@ -788,6 +882,83 @@ val d = IrishSetter("Big Red") // "Big Red is a Dog"
788882
{% endtab %}
789883
{% endtabs %}
790884

885+
### Overriding an implemented method
886+
887+
A class can also override a method that is defined in a trait. Here is an example:
888+
889+
{% tabs traits_6 class=tabs-scala-version %}
890+
{% tab 'Scala 2' %}
891+
```scala
892+
class Cat extends HasLegs with HasTail {
893+
val numLegs = 4
894+
val tailColor = "Tabby"
895+
def walk() = println("I’m not walking")
896+
// override 'stop'
897+
override def stop() = println("I'm still not walking")
898+
}
899+
```
900+
{% endtab %}
901+
{% tab 'Scala 3' %}
902+
```scala
903+
class Cat extends HasLegs, HasTail:
904+
val numLegs = 4
905+
val tailColor = "Tabby"
906+
def walk() = println("I’m not walking")
907+
// override 'stop'
908+
override def stop() = println("I'm still not walking")
909+
```
910+
{% endtab %}
911+
{% endtabs %}
912+
913+
The REPL shows how this works:
914+
915+
{% tabs traits_7 class=tabs-scala-version %}
916+
{% tab 'Scala 2' %}
917+
```scala
918+
scala> val c = new Cat
919+
val c: Cat = Cat@3855b27e
920+
921+
scala> c. walk()
922+
I’m not walking
923+
924+
scala> c.stop()
925+
I'm still not walking
926+
```
927+
{% endtab %}
928+
{% tab 'Scala 3' %}
929+
```scala
930+
scala> val c = Cat()
931+
val c: Cat = Cat@539ee811
932+
933+
scala> c.walk()
934+
I’m not walking
935+
936+
scala> c.stop()
937+
I'm still not walking
938+
```
939+
{% endtab %}
940+
{% endtabs %}
941+
942+
You can also override a method when creating an instance of a class:
943+
944+
{% tabs traits_8 class=tabs-scala-version %}
945+
{% tab 'Scala 2' %}
946+
```scala
947+
val c = new Cat {
948+
override def walk() = println("Run")
949+
override def stop() = println("I will continue to run")
950+
}
951+
```
952+
{% endtab %}
953+
{% tab 'Scala 3' %}
954+
```scala
955+
val c = new Cat:
956+
override def walk() = println("Run")
957+
override def stop() = println("I will continue to run")
958+
```
959+
{% endtab %}
960+
{% endtabs %}
961+
791962
This is just a taste of what you can accomplish with traits.
792963
For more details, see the remainder of these modeling lessons.
793964

_overviews/scala3-book/fun-partial-functions.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,13 @@ val res = List(1, 2, 3).collect({ case i if i % 2 == 1 => i * 2 }) // List(2, 6)
5757

5858
You can define a default value for arguments not in domain with `applyOrElse`:
5959

60-
{% tabs fun-partial-5 %}
61-
{% tab 'Scala 2 and 3' %}
60+
{% tabs fun-partial-5 class=tabs-scala-version%}
61+
{% tab 'Scala 2' %}
62+
```scala
63+
doubledOdds.applyOrElse(4, (i: Int) => i + 1) // 5
64+
```
65+
{% endtab %}
66+
{% tab 'Scala 3' %}
6267
```scala
6368
doubledOdds.applyOrElse(4, _ + 1) // 5
6469
```

0 commit comments

Comments
 (0)