2010-07-28 18 views
5

Ho trovato un errore nel mio codice di scala, che mi lascia perplesso. Di seguito è una versione semplificata del problema.Il metodo astratto Scala è nullo nella superclasse quando la sottoclasse lo implementa usando val?

Nel costruttore di una classe astratta, voglio verificare alcune affermazioni sui metodi astratti. Così, quando viene creato un oggetto di una sottoclasse, questi assert sono controllati, per vedere se tutto è implementato come dovrebbe.

va male quando la sottoclasse implementa un metodo astratto utilizzando un "val", tuttavia: il codice

Scala:

abstract class A { 
    def aval : String 
    assert(aval != null, "aval == null") 
    assert(aval == "B", "aval: "+aval) 
} 

class B extends A { 
    def aval = "B" 
} 

class C extends A { 
    val aval = "B" 
} 

object VariousScalaTests { 
    def main(args : Array[String]) : Unit = { 
     val b = new B 
     val c = new C 
    } 
} 

Scala Errore:

Exception in thread "main" java.lang.AssertionError: assertion failed: aval == null 
    at scala.Predef$.assert(Predef.scala:92) 
    at A.<init>(VariousScalaTests.scala:4) 
    at C.<init>(VariousScalaTests.scala:12) 
    at VariousScalaTests$.main(VariousScalaTests.scala:19) 
    at VariousScalaTests.main(VariousScalaTests.scala) 

Quindi non riesce a ultima riga di codice: "val c = new C". La classe B funziona perfettamente, ma la classe C no! L'unica differenza è che C implementa aval usando "val" e B usando "def".

Quindi la mia domanda, soprattutto, perché questa differenza? Non capisco cosa sta succedendo.

E c'è un modo per farlo funzionare come voglio in entrambi i casi in scala? O sto semplicemente perdendo un modo più elegante per affermare ciò che voglio in scala?

risposta

5

Questo codice Java equivalente dovrebbe spiegare il problema:

public abstract class A { 
    public String aval(); 
} 

public class B extends A { 
    public String aval() { 
     return "B"; 
    } 
} 

public class C extends A { 
    private String _aval; 

    public C() { 
     _aval = "B"; 
    } 

    public String aval() { 
     return _aval; 
    } 
} 

Quando si esegue

val c = new C 

il costruttore di A piste prima il costruttore del C e il campo _aval non è stato assegnato ancora. Quindi il metodo aval() restituisce null (il valore iniziale del campo _aval). Ma in

val b = new B 

non esiste alcun problema.

In generale, you should try to avoid calling virtual methods from a constructor.

And is there a way to make it work as I want in both cases in scala?

Vedi this question per alcuni approcci.

+0

il codice Java spiega bene! Vedo ora ... Considerando un comportamento come questo, "evitare di chiamare metodi virtuali da un costruttore" sembra un buon consiglio. Sicuramente in scala, come nel codice java, è molto più chiaro cosa sta succedendo. Cercherò in un altro modo di controllare elegantemente le sottoclassi nel mio caso specifico, il tuo link potrebbe aiutarti. Almeno il mistero di questo errore è sparito ora ;-) –

6

In Scala è possibile utilizzare le definizioni primi funzione per causare la val di sottoclasse inizializzato prima costruttore eccellente si chiama:

class C extends { 
    val aval = "B" 
} with A 
+0

Questa funzione si chiama Campi Preinizializzati, giusto? – missingfaktor

+0

Non sicuro. In scala lang spec sezione 5.1.6 si chiama "Early definitions". – venechka

Problemi correlati