Scala def override with val throws NPE

I study Scala and in Programming in Scala 3rd Ed , Ch 10, Page 225 , section Overriding methods and fields , he says

The principle of single access is only one aspect in which Scala treats fields and methods more evenly than Java. Another difference is that in Scala, fields and methods belong to the same namespace. This allows the field to override the parameterless method. For example, you can change the implementation of the content in the ArrayElement class from method to field without changing the abstract definition of the content method in the Element class, as shown in Listing 10.4:

My code based on an example,

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 redefined 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?

+5
source share
1 answer

Class level fields are initialized before anything else, which means that null is assigned. You can make a lazy val declaration and it will not be initialized until it is called. This is the reason def works. Best of all, instead of creating a private class field for a private constructor field, simply make the constructor field publicly available as follows:

 class ArrayElement(val contnts: Array[String]) extends Element {} 

Since there is a parent class, it would be nice to mark it as an override;

 class ArrayElement(override val contnts: Array[String]) extends Element {} 

If this is a stateless data container class, the best option is to make it a case class that (among several other things) has default fields.

 case class ArrayElement(override val contnts: Array[String]) extends Element 

This is a much more idiomatic scala, and it will provide you with equals , hashCode based on values, pattern matching, simpler construction (no new needed)

+7
source

All Articles