2009-12-11 6 views
8

Dans Scala, comment définir un paramètre local dans le constructeur primaire d'une classe qui n'est pas un membre de données et qui, par exemple, sert uniquement à initialiser un membre de données dans la classe de base?Dans Scala, comment définissez-vous un paramètre local dans le constructeur primaire d'une classe?

Par exemple, dans le code suivant, comment pourrais-je définir correctement le paramètre b dans le constructeur principal de classe B afin qu'il ne génère un paramètre local temporaire et non un membre de données?

class A(var a: Int) 
class B(?b?) extends A(b) 

Randall, vos réponses expliquent pourquoi le compilateur Scala se plaint quand je présente une méthode inc qui incrémente la propriété a, mais aussi changer le nom du paramètre dans le constructeur de la classe B pour correspondre à celle de la paramètre dans la classe A constructeur:

class A(var a: Int) 
class B(a: Int) extends A(a) { 
    def inc(value: Int) { this.a += value } 
} 

Scala sortie du compilateur:

$ scala construct.scala 
construct.scala:3: error: reassignment to val 
    def inc(value: Int) { this.a += value } 
          ^
one error found 

Scala se plaint parce que la classe B doit maintenant avoir une propriété privée en lecture seule a en raison de la référence à a dans inc. La modification B(a: Int)-B(var a: Int) génère une erreur de compilateur différent:

construct.scala:2: error: error overriding variable a in class A of type Int; 
variable a needs `override' modifier 
class B(var a: Int) extends A(a) { 
      ^
one error found 

Ajout override ne fonctionne pas, soit:

construct.scala:2: error: error overriding variable a in class A of type Int; 
variable a cannot override a mutable variable 
class B(override var a: Int) extends A(a) { 
       ^
one error found 

Comment puis-je utiliser le même nom dans le paramètre dans le constructeur principal de B comme la propriété définie dans le constructeur principal de la classe de base A?

Répondre

16

Si vous supprimez le mot clé "var" ou "val" du paramètre constructeur, il ne génère pas de propriété. Sachez cependant que les paramètres du constructeur non-var, non-val sont inclus dans le champ d'application et accessibles dans toute la classe. Si vous en utilisez un dans un code non-constructeur (ie, dans le corps d'une méthode), il y aura un champ privé invisible dans la classe générée qui contient ce paramètre constructeur, comme si vous l'aviez fait un "private var" ou " paramètre constructeur privé ".

Addendum (mieux vaut tard que jamais ??):

Dans ce code, les références au paramètre du constructeur ne se produisent que dans le corps du constructeur:

class C1(i: Int) { 
    val iSquared = i * i 
    val iCubed = iSquared * i 
    val iEven = i - i % 2 
} 

... Ici, la valeur i n'existe que pendant l'exécution du constructeur.Cependant, dans le code suivant, étant donné que le paramètre constructeur est référencé dans un corps de méthode qui ne fait pas partie du corps du constructeur, le paramètre constructeur doit être copié dans un champ (privé) de la classe générée (augmentant sa taille). exigence de mémoire par les 4 octets nécessaires pour tenir une Int):

class C2(i: Int) { 
    val iSquared = i * i 
    val iCubed = iSquared * i 
    val iEven = i - i % 2 

    def mod(d: Int) = i % d 
} 
+0

Randall, je vous remercie de votre réponse. Pouvez-vous s'il vous plaît fournir un exemple de votre mise en garde? –

+0

Il n'est donc pas possible d'avoir un paramètre pour le constructeur sans que ce paramètre devienne un attribut pour l'instance? J'ai une classe qui reçoit un booléen, et l'initialisation varie en fonction de la valeur booléenne, mais je n'ai pas besoin de la valeur booléenne après la fin du constructeur. Cependant, les instances de cette classe auront toutes un attribut supplémentaire (le booléen mentionné ci-dessus) occupant de la mémoire, même lorsqu'elles ne sont pas nécessaires au-delà de l'initialisation. C'est vraiment nul, pour une langue aussi bonne que Scala. Triste :( – Ernesto

+1

@Ernesto, oui, comme Randall l'a expliqué dans son addendum, il est possible d'avoir un paramètre constructeur local, à condition qu'aucune méthode dans la classe ne se réfère à ce paramètre.Si une telle méthode existe, Scala "promeut" le paramètre à un membre de données d'instance –

4

Après quelques essais, j'ai déterminé que tout simplement en laissant de côté var ou val devant le paramètre b fera un paramètre local et non un membre de données :

class A(var a: Int) 
class B(b: Int) extends A(b) 

extension Java:

$ javap -private B 
Compiled from "construct.scala" 
public class B extends A implements scala.ScalaObject{ 
    public B(int); 
} 

$ javap -private A 
Compiled from "construct.scala" 
public class A extends java.lang.Object implements scala.ScalaObject{ 
    private int a; 
    public A(int); 
    public void a_$eq(int); 
    public int a(); 
    public int $tag()  throws java.rmi.RemoteException; 
} 

Notez que la classe A a un membre de données privées a en raison du var a: Int dans son constructeur principal. Cependant, la classe B ne possède aucun membre de données, mais son constructeur principal a toujours un seul paramètre entier.

+0

Il semblerait que si vous faites un B cas de classe, b sera un membre de données.Toute idée pourquoi? –

+0

J'ai fait quelques expérimentations, et je suis arrivé à la conclusion inverse.Même quand ne pas préfixer le paramètre avec 'val' ou' var', il est toujours conservé au-delà de la constr uctor/phase d'initialisation de l'instance. Sinon, comment expliquez-vous que ce code compile et s'exécute? (https://gist.github.com/gnapse/96387a056f0cf45dac7a) Notez comment le paramètre 'fake' du constructeur est accessible dans la méthode de l'instance' output'. – Ernesto

+0

@Ernesto, quelle version du compilateur Scala avez-vous utilisé? J'ai effectué ce test il y a presque cinq ans, probablement en utilisant la dernière version à l'époque. Le comportement a peut-être changé depuis. –

3

Derek,

Si vous avez ceci:

class A(a: Int) { 
    val aa = a // reference to constructor argument in constructor code (no problem) 
    def m: Float = a.toFloat // reference to constructor argument in method body (causes a to be held in a field) 
} 

vous trouverez (en utilisant javap, par exemple) qu'un champ nommé "un" est présent dans la classe. Si vous commentez le "def m", vous verrez alors que le champ n'est pas créé.

4

Vous pouvez créer des variables temporaires tout au long de l'initialisation des membres de la classe unique comme celui-ci:

class A(b:Int){ 
    val m = { 
    val tmp = b*b 
    tmp+tmp 
    } 
} 
+0

Donc 'm' est une propriété qu'une instance de la classe' A' initialise à '2 * b * b'? –

+0

Exactement. Vous pouvez également envelopper quelque chose entre accolades à l'intérieur du constructeur. Peut-être que les locaux instanciés ne seront pas créés en tant que membres de la classe. – ziggystar

Questions connexes