20 votes

Les membres de la classe Scala et les paramètres du constructeur sont en conflit de nom.

Considérez la classe suivante écrite en Java:

class NonNegativeDouble {
    private final double value;
    public NonNegativeDouble(double value) {
        this.value = Math.abs(value);
    }
    public double getValue() { return value; }
}

Elle définit un champ final appelé value qui est initialisé dans le constructeur, en prenant son paramètre appelé de manière similaire et en lui appliquant une fonction.

Je veux écrire quelque chose de similaire en Scala. Au début, j'ai essayé:

class NonNegativeDouble(value: Double) {
  def value = Math.abs(value)
}

Mais le compilateur se plaint: error: overloaded method value needs result type

Évidemment, le compilateur pense que l'expression value à l'intérieur de l'expression Math.abs(value) se réfère à la méthode en cours de définition. Par conséquent, la méthode en cours de définition est récursive, donc je dois indiquer son type de retour. Ainsi, le code que j'ai écrit ne fait pas ce que j'attendais : je voulais que value à l'intérieur de Math.abs(value) se réfère au paramètre du constructeur value, et non à la méthode en cours de définition. C'est comme si le compilateur ajoutait implicitement un this. à Math.abs(this.value).

Ajouter val ou var (ou des variantes private ...) au paramètre du constructeur ne semble pas aider.

Donc, ma question est : puis-je définir une propriété avec le même nom qu'un paramètre de constructeur, mais peut-être une valeur différente? Si oui, comment? Sinon, pourquoi?

Merci!

18voto

Daniel C. Sobral Points 159554

Non, vous ne pouvez pas. En Scala, les paramètres du constructeur sont des propriétés, il n'a donc pas de sens de les redéfinir.

La solution, bien sûr, est d'utiliser un autre nom :

class NonNegativeDouble(initValue: Double) {
  val value = Math.abs(initValue)
}

Utilisé de cette manière, initValue ne fera pas partie des instances créées. Cependant, si vous l'utilisez dans un def ou une déclaration de correspondance de motifs, alors il devient une partie de chaque instance de la classe.

4voto

soulmachine Points 67

@Daniel C. Sobral

class NonNegativeDouble(initValue: Double) {
  val value = Math.abs(initValue)
}

Votre code est correct, mais "les paramètres du constructeur sont des propriétés", ce n'est pas vrai.

Un article du site officiel dit,

Un paramètre tel que class Foo(x : Int) est transformé en champ s'il est référencé dans une ou plusieurs méthodes

Et la réponse de Martin confirme sa véracité:

Tout cela est vrai, mais cela doit être traité comme une technique d'implémentation. C'est pourquoi la spécification n'en parle pas.

Donc normalement, nous pouvons toujours traiter les paramètres du constructeur principal comme des paramètres de méthode normale, mais lorsque les paramètres sont référencés par l'une des méthodes, le compilateur les convertira intelligemment en champ privé.

Si un paramètre formel est précédé de val, le compilateur génère automatiquement une définition de getter. Si var, il génère en plus un setter. Voir la section 5.3 de la spécification du langage.

C'est tout ce qu'il y a à savoir sur les paramètres du constructeur principal.

2voto

Jan Pavtel Points 96

Vous pouvez considérer le champ paramétrique

class NonNegativeDouble(val value: Double, private val name: String ){
  if (value < 0) throw new IllegalArgumentException("la valeur ne peut pas être négative")
  override def toString = 
    "NonNegativeDouble(value = %s, name = %s)" format (value, name)
}

val tom = "Tom"
val k = -2.3

val a = new NonNegativeDouble(k.abs, tom)
a: NonNegativeDouble = NonNegativeDouble(value = 2.3, name = Tom)

a.value
res13: Double = 2.3

a.name
:12: error: value name in class NonNegativeDouble cannot be accessed in NonNegativeDouble
     a.name

val b = new NonNegativeDouble(k, tom)
java.lang.IllegalArgumentException: la valeur ne peut pas être négative
...

Cela définit des champs et des paramètres avec les mêmes noms "value", "name". Vous pouvez ajouter des modificateurs tels que private ...

0voto

XioRcaL Points 101

Dans le cas des case classes, cela devrait être :

case class NonNegativeDouble(private val initValue: Double) {
  val value = Math.abs(initValue)
  def copy(value: Double = this.value) = NonNegativeDouble(value)
}

L'implémentation de copy est nécessaire pour empêcher la version synthétisée du compilateur de lier l'argument initValue.

Je m'attends à ce que le compilateur soit suffisamment intelligent pour ne pas conserver l'« espace supplémentaire » pour initValue. Je n'ai pas vérifié ce comportement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X