2016-05-27 14 views
5

sto imparando Scala e in Programming in Scala 3rd Ed, Ch 10, Page 225, sezione Overriding methods and fields, si diceScala sovrascrivendo definizione con val getta NPE

Il principio di accesso uniforme è solo uno degli aspetti in cui Scala tratta campi e metodi più uniformemente di Java . Un'altra differenza è che in Scala, i campi e i metodi appartengono allo stesso spazio dei nomi. Questo consente a un campo di sovrascrivere un metodo senza parametri. Per esempio, si potrebbe modificare l'implementazione dei contenuti in classe ArrayElement da un metodo a un campo, senza dover modificare il astratta definizione del metodo di contenuti in classe elemento, come mostrato nel Listato 10.4:

mio codice basato sull'esempio è

con 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 

ottengo

ae: ArrayElement = [email protected] 
res0: Int = 2 
res1: Int = 5 

con def sovrascritti come 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 

ottengo NPE come

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) 

Che cosa mi manca?

risposta

7

I campi a livello di classe vengono inizializzati prima di ogni altra cosa, ovvero viene assegnato null. È possibile effettuare la dichiarazione a lazy val e non verrà inizializzata finché non viene chiamata. Questa è la ragione per cui la def funziona. Un modo migliore, però, invece di creare un campo pubblico di classe shadowing campo costruttore privato, è quello di fare solo il campo costruttore pubblico in questo modo:

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

Dal momento che c'è una classe genitore in gioco anche qui, sarebbe bene per contrassegnarlo come prioritario;

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

Se questo sta per essere un apolide classe contenitore di dati, però, l'opzione migliore è quello di renderlo un case class, che (tra le molte altre cose) ha campi pubblici-by-default.

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

Questo è molto più idiomatica scala e vi fornirà un valore a base equals, hashCode, pattern matching, più semplice la costruzione (non c'è bisogno di new)

+1

Vero. Una semplice regola empirica è di non fare mai riferimento a un 'def' astratto all'interno di' val', poiché non ci sono garanzie che 'def' abbia un valore nel momento in cui i' val's vengono istanziati. @ Denenyth, ho modificato il tuo commento per fornire un'altra opzione – Sergey

Problemi correlati