2010-07-28 8 views
5

J'ai trouvé une erreur dans mon code scala, ce qui m'intrigue. Voici une version simplifiée du problème.Scala méthode abstraite est nulle dans la superclasse lorsque la sous-classe l'implémente en utilisant val?

Dans le constructeur d'une classe abstraite, je veux vérifier quelques affirmations sur les méthodes abstraites. Ainsi, lorsqu'un objet d'une sous-classe est créé, ces assertions sont vérifiées, pour voir si tout est implémenté comme il se doit.

Il va mal quand la sous-classe implémente une méthode abstraite en utilisant un "val" cependant:

Code 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 Erreur:

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) 

Il échoue au dernière ligne de code: "val c = new C". La classe B fonctionne parfaitement, mais pas la classe C! La seule différence est que C implémente l'aval en utilisant "val" et B en utilisant "def".

Donc, ma question, surtout, pourquoi cette différence? Je ne comprends pas ce qui se passe.

Et y a-t-il un moyen de le faire fonctionner comme je veux dans les deux cas dans scala? Ou est-ce que je manque juste une manière plus élégante d'affirmer ce que je veux dans Scala?

Répondre

5

Ce code équivalent Java doit expliquer le problème:

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

Lorsque vous exécutez

val c = new C 

le constructeur de A exécute avant le constructeur de C et le champ _aval n'est pas affecté encore. Ainsi, la méthode aval() renvoie null (la valeur initiale du champ _aval). Mais dans

val b = new B 

il n'y a pas un tel problème.

En général, 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?

Voir this question pour certaines approches.

+0

Votre code java explique bien! Je vois maintenant ... Considérant un comportement comme celui-ci, "évitez d'appeler des méthodes virtuelles à partir d'un constructeur" semble bon même des conseils. Certes, dans Scala, comme dans le code Java, il est beaucoup plus clair ce qui se passe. Je vais chercher un autre moyen de vérifier élégamment les sous-classes dans mon cas spécifique, votre lien pourrait vous aider. Au moins le mystère de cette erreur a disparu maintenant ;-) –

6

Scala vous pouvez utiliser les définitions premières fonctionnalité pour provoquer la val de la sous-classe initialisé avant constructeur super est appelé:

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

Cette fonctionnalité est appelée champs pré-initialisés, non? – missingfaktor

+0

Pas sûr. Dans scala lang spec section 5.1.6, il est appelé "Early definitions". – venechka

Questions connexes