-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Scala 2 Book migration - Scala Classes #3128
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -97,26 +97,100 @@ p.vocation = "Musician" | |
{% endtab %} | ||
{% endtabs %} | ||
|
||
### Fields and methods | ||
### `val` makes fields read-only | ||
|
||
Classes can also have methods and additional fields that are not part of constructors. | ||
They are defined in the body of the class. | ||
The body is initialized as part of the default constructor: | ||
In that first example both fields were defined as `var` fields: | ||
|
||
{% tabs class_val_fields_1 %} | ||
{% tab 'Scala 2 and 3' %} | ||
```scala | ||
class Person(var name: String, var vocation: String) | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
||
That makes those fields mutable. You can also define them as `val` fields, which makes them immutable: | ||
|
||
{% tabs class_val_fields_2 %} | ||
{% tab 'Scala 2 and 3' %} | ||
```scala | ||
class Person(val name: String, val vocation: String) | ||
// --- --- | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
||
If you now try to change the name of a `Person` instance, you’ll see an error: | ||
|
||
{% tabs class_val_fields_3 class=tabs-scala-version %} | ||
{% tab 'Scala 2' %} | ||
```scala | ||
scala> class Person(val name: String, val vocation: String) | ||
class Person | ||
|
||
scala> val p = new Person("Robert Allen Zimmerman", "Harmonica Player") | ||
val p: Person = Person@1c58d7be | ||
|
||
scala> p.name = "Bob Dylan" | ||
^ | ||
error: reassignment to val | ||
``` | ||
{% endtab %} | ||
{% tab 'Scala 3' %} | ||
```scala | ||
scala> class Person(val name: String, val vocation: String) | ||
// defined class Person | ||
|
||
scala> val p = Person("Robert Allen Zimmerman", "Harmonica Player") | ||
val p: Person = Person@779228dc | ||
|
||
scala> p.name = "Bob Dylan" | ||
-- [E052] Type Error: ---------------------------------------------------------- | ||
1 |p.name = "Bob Dylan" | ||
|^^^^^^^^^^^^^^^^^^^^ | ||
|Reassignment to val name | ||
| | ||
| longer explanation available when compiling with `-explain` | ||
1 error found | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
||
### Class constructors | ||
|
||
In Scala, the primary constructor of a class is a combination of: | ||
|
||
- The constructor parameters | ||
- Methods that are called in the body of the class | ||
- Statements and expressions that are executed in the body of the class | ||
|
||
Defining parameters in the primary constructor automatically creates fields in the class, and fields declared in the body | ||
of a Scala class are handled in a manner similar to Java; they are assigned when the class is first instantiated. | ||
|
||
This `Person` class demonstrates several of the things you can do inside the body of a class: | ||
|
||
{% tabs method class=tabs-scala-version %} | ||
{% tab 'Scala 2' %} | ||
|
||
```scala | ||
class Person(var firstName: String, var lastName: String) { | ||
|
||
println("initialization begins") | ||
// 'public' access by default | ||
val fullName = firstName + " " + lastName | ||
|
||
// a private field | ||
private val HOME = System.getProperty("user.home") | ||
|
||
// a class method | ||
def printFullName: Unit = | ||
// access the `fullName` field, which is created above | ||
println(fullName) | ||
|
||
def printHome: Unit = println(s"HOME = $HOME") | ||
|
||
// an overridden method | ||
override def toString(): String = fullName | ||
|
||
printFullName | ||
println("initialization ends") | ||
} | ||
|
@@ -128,15 +202,23 @@ class Person(var firstName: String, var lastName: String) { | |
|
||
```scala | ||
class Person(var firstName: String, var lastName: String): | ||
|
||
println("initialization begins") | ||
// 'public' access by default | ||
val fullName = firstName + " " + lastName | ||
|
||
// a private field | ||
private val HOME = System.getProperty("user.home") | ||
|
||
// a class method | ||
def printFullName: Unit = | ||
// access the `fullName` field, which is created above | ||
println(fullName) | ||
|
||
def printHome: Unit = println(s"HOME = $HOME") | ||
|
||
// an overridden method | ||
override def toString(): String = fullName | ||
|
||
printFullName | ||
println("initialization ends") | ||
``` | ||
|
@@ -153,10 +235,16 @@ scala> val john = new Person("John", "Doe") | |
initialization begins | ||
John Doe | ||
initialization ends | ||
val john: Person = Person@55d8f6bb | ||
val john: Person = John Doe | ||
|
||
scala> john.printFullName | ||
John Doe | ||
|
||
scala> john.printHome | ||
HOME = /Users/al | ||
|
||
scala> println(john) | ||
John Doe | ||
```` | ||
{% endtab %} | ||
{% tab 'Scala 3' %} | ||
|
@@ -165,10 +253,16 @@ scala> val john = Person("John", "Doe") | |
initialization begins | ||
John Doe | ||
initialization ends | ||
val john: Person = Person@55d8f6bb | ||
val john: Person = John Doe | ||
|
||
scala> john.printFullName | ||
John Doe | ||
|
||
scala> john.printHome | ||
HOME = /Users/al | ||
|
||
scala> println(john) | ||
John Doe | ||
```` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
@@ -788,6 +882,83 @@ val d = IrishSetter("Big Red") // "Big Red is a Dog" | |
{% endtab %} | ||
{% endtabs %} | ||
|
||
### Overriding an implemented method | ||
|
||
A class can also override a method that is defined in a trait. Here is an example: | ||
Comment on lines
+885
to
+887
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This covers both |
||
|
||
{% tabs traits_6 class=tabs-scala-version %} | ||
{% tab 'Scala 2' %} | ||
```scala | ||
class Cat extends HasLegs with HasTail { | ||
val numLegs = 4 | ||
val tailColor = "Tabby" | ||
def walk() = println("I’m not walking") | ||
// override 'stop' | ||
override def stop() = println("I'm still not walking") | ||
} | ||
``` | ||
{% endtab %} | ||
{% tab 'Scala 3' %} | ||
```scala | ||
class Cat extends HasLegs, HasTail: | ||
val numLegs = 4 | ||
val tailColor = "Tabby" | ||
def walk() = println("I’m not walking") | ||
// override 'stop' | ||
override def stop() = println("I'm still not walking") | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
||
The REPL shows how this works: | ||
|
||
{% tabs traits_7 class=tabs-scala-version %} | ||
{% tab 'Scala 2' %} | ||
```scala | ||
scala> val c = new Cat | ||
val c: Cat = Cat@3855b27e | ||
|
||
scala> c. walk() | ||
I’m not walking | ||
|
||
scala> c.stop() | ||
I'm still not walking | ||
``` | ||
{% endtab %} | ||
{% tab 'Scala 3' %} | ||
```scala | ||
scala> val c = Cat() | ||
val c: Cat = Cat@539ee811 | ||
|
||
scala> c.walk() | ||
I’m not walking | ||
|
||
scala> c.stop() | ||
I'm still not walking | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
||
You can also override a method when creating an instance of a class: | ||
|
||
{% tabs traits_8 class=tabs-scala-version %} | ||
{% tab 'Scala 2' %} | ||
```scala | ||
val c = new Cat { | ||
override def walk() = println("Run") | ||
override def stop() = println("I will continue to run") | ||
} | ||
``` | ||
{% endtab %} | ||
{% tab 'Scala 3' %} | ||
```scala | ||
val c = new Cat: | ||
override def walk() = println("Run") | ||
override def stop() = println("I will continue to run") | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
||
This is just a taste of what you can accomplish with traits. | ||
For more details, see the remainder of these modeling lessons. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,8 +57,13 @@ val res = List(1, 2, 3).collect({ case i if i % 2 == 1 => i * 2 }) // List(2, 6) | |
|
||
You can define a default value for arguments not in domain with `applyOrElse`: | ||
|
||
{% tabs fun-partial-5 %} | ||
{% tab 'Scala 2 and 3' %} | ||
{% tabs fun-partial-5 class=tabs-scala-version%} | ||
{% tab 'Scala 2' %} | ||
```scala | ||
doubledOdds.applyOrElse(4, (i: Int) => i + 1) // 5 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A small fix which I omitted last time somehow. |
||
``` | ||
{% endtab %} | ||
{% tab 'Scala 3' %} | ||
```scala | ||
doubledOdds.applyOrElse(4, _ + 1) // 5 | ||
``` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intuitively, after seeing the syntax used above, someone could expect this to be defined in Scala 3 as:
It seems like only the Scala 2 syntax is accepted, though.