I am learning Scala
and in Programming in Scala 3rd Ed
, Ch 10, Page 225
, section Overriding methods and fields
, it says
The uniform access principle is just one aspect where Scala treats fields and methods more uniformly than Java. Another difference is that in Scala, fields and methods belong to the same namespace. This makes it possible for a field to override a parameterless method. For instance, you could change the implementation of contents in class ArrayElement from a method to a field without having to modify the abstract method definition of contents in class Element, as shown in Listing 10.4:
My code based on the example is
with def
abstract class Element {
def contents: Array[String]
val height = contents.length
val width = if (height == 0) 0 else contents(0).length
}
class ArrayElement(contnts: Array[String]) extends Element {
def contents: Array[String] = contnts
}
// --
val ae = new ArrayElement(Array("hello", "world"))
ae.height
ae.width
I get
ae: ArrayElement = ArrayElement@7cd3ba8e
res0: Int = 2
res1: Int = 5
with def overridden as val in ArrayElement
abstract class Element {
def contents: Array[String]
val height = contents.length
val width = if (height == 0) 0 else contents(0).length
}
class ArrayElement(contnts: Array[String]) extends Element {
val contents: Array[String] = contnts
}
// --
val ae = new ArrayElement(Array("hello", "world"))
ae.height
ae.width
I get NPE
as
java.lang.NullPointerException
at #worksheet#.Element.<init>(scratch.scala:4)
at #worksheet#.ArrayElement.<init>(scratch.scala:10)
at #worksheet#.ae$lzycompute(scratch.scala:15)
at #worksheet#.ae(scratch.scala:15)
at #worksheet#.#worksheet#(scratch.scala:14)
What am I missing?
Class-level fields are initialized before anything else, meaning that null
is assigned. You can make the declaration a lazy val
and it won't be initialized until it's called. That's the reason the def works. A better way though, instead of creating a class public field shadowing the private constructor field, is to just make the constructor field public like this:
class ArrayElement(val contnts: Array[String]) extends Element {}
Since there's a parent class at play here too, it would be good to mark it as overriding;
class ArrayElement(override val contnts: Array[String]) extends Element {}
If this is going to be a stateless data container class though, the best option is to make it a case class
, which (among several other things) has public-by-default fields.
case class ArrayElement(override val contnts: Array[String]) extends Element
This is much more idiomatic scala and it will provide you with a value-based equals
, hashCode
, pattern matching, simpler construction (no need to new
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With