2010-07-28 9 views
5

Znalazłem błąd w moim kodzie scala, który mnie zastanawia. Poniżej znajduje się uproszczona wersja problemu.Metoda abstrakcyjna Scala ma wartość zerową w nadklasie, gdy jej podklasa implementuje za pomocą val?

W konstruktorze klasy abstrakcyjnej chcę sprawdzić kilka twierdzeń na temat metod abstrakcyjnych. W ten sposób, gdy tworzony jest obiekt podklasy, te potwierdzenia są sprawdzane, aby sprawdzić, czy wszystko jest zaimplementowane tak, jak powinno.

To idzie źle, gdy podklasa implementuje metody abstrakcyjne używając "val" Jednakże: kod

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 błąd:

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) 

Więc nie powiedzie u ostatnia linia kodu: "val c = new C". Klasa B działa idealnie, ale klasa C nie! Jedyna różnica polega na tym, że C implementuje aval używając "val" i B używając "def".

Moje pytanie, przede wszystkim, dlaczego ta różnica? Nie rozumiem, co się dzieje.

Czy istnieje sposób, aby działał tak, jak chcę w obu przypadkach w scala? Czy po prostu brakuje mi bardziej eleganckiego sposobu potwierdzania tego, co chcę w scala?

Odpowiedz

5

To odpowiednik kodu Java powinien wyjaśnić problem:

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; 
    } 
} 

Po uruchomieniu

val c = new C 

konstruktora A tras przed konstruktor C a pole _aval nie jest przypisany jeszcze. Zatem metoda aval() zwraca null (wartość początkowa pola _aval). Ale w tym przypadku nie ma takiego problemu.

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

Zobacz this question dla niektórych podejść.

+0

Twój kod Java wyjaśnia ładnie! Rozumiem teraz ... Rozważanie takich zachowań, jak "unikanie wywoływania wirtualnych metod od konstruktora" wydaje się dobrą radą. Z pewnością w scala, podobnie jak w kodzie java, jest o wiele bardziej jasne, co się dzieje. Poszukuję innego sposobu, aby elegancko sprawdzić podklasy w moim konkretnym przypadku, twój link może pomóc. Przynajmniej tajemnica tego błędu już minęła ;-) –

6

W Scali można użyć wczesne definicje funkcji, aby spowodować val podklasy za zainicjowany przed Super konstruktor nazywa się:

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

Ta funkcja nazywa się wstępnie zainicjalizowane pola, prawda? – missingfaktor

+0

Nie jestem pewien. W scala lang spec sekcja 5.1.6 nazywa się "Early Definitions". – venechka

Powiązane problemy