3 votes

Désambiguïser les paramètres du constructeur dont le nom est identique à celui du champ "class" de la superclasse

En jouant avec Scala, j'avais un code comme celui-ci :

class Superclass(var i : Int){}

class Subclass(i : Int) extends Superclass(0) {
   print(i)
}

J'ai découvert que print(i) imprime le paramètre du constructeur i de Subclass(i : Int)

Ma question est la suivante : dans une telle situation, comment puis-je accéder au champ i de Superclass ?

6voto

Mario Galic Points 3246

Type d'inscription peut faire évoluer le type de this ce qui permet de désambiguïser efficacement les deux identifiants

class Subclass(i : Int) extends Superclass(0) {
  print((this: Superclass).i)
  print(i)
}

Par ailleurs, il existe également ce qui suit syntaxe qui pourrait être utilisée dans le cas des membres de méthodes (et qui n'est peut-être pas bien connue)

super[ClassQualifier]

Prenons l'exemple suivant

trait A {
  def f = "A"
}

trait B extends A {
  override def f = "B"
}

class C extends A with B {
  println(super[A].f)
  println(super[B].f)
  println(f)

  override def f = "C"
}

new C
// A
// B
// C

3voto

Dmytro Mitin Points 13413

@MarioGalic a répondu à cette question. Je me contenterai de faire quelques ajouts qui sont trop longs pour les commentaires.

Le malentendu le plus courant est que i en

class MyClass(i : Int)

n'est qu'un paramètre du constructeur et non un champ. En fait, si nous faisons

import scala.reflect.runtime.universe._
println(reify{
  class MyClass(i : Int)
}.tree) 

nous verrons (Scala 2.13.2)

{
  class MyClass extends AnyRef {
    <paramaccessor> private[this] val i: Int = _;
    def <init>(i: Int) = {
      super.<init>();
      ()
    }
  };
  ()
}

Ainsi, un paramètre du constructeur primaire sans val / var génère private[this] domaine. Ainsi, le

class MyClass(i : Int)

est similaire à

class MyClass(private[this] val i : Int)

et n'est PAS similaire à la fonction

public class MyClass { 
  public MyClass(int i) {
  }
}

sans champs.

Nous pouvons vérifier que i est un champ qui s'y réfère avec this dans le corps de la classe

class MyClass(i : Int) {
  println(this.i)
}
new MyClass(1) // prints 1

Le champ i est private[this] nous ne pouvons donc pas y faire référence en dehors du corps de la classe (ou à l'intérieur du corps sur une instance différente de this )

class MyClass(i : Int) {
  //println(new MyClass(2).i) //doesn't compile
}
//new MyClass(1).i //doesn't compile

Je n'ai pas trouvé de place appropriée dans la spécification de Scala mais ce comportement est bien connu depuis longtemps. Par exemple, dans "Scala for the impatient" de Cay S. Horstmann, il est écrit (édition 2, section 5.7) :

Les paramètres de construction peuvent également être des paramètres de méthode ordinaires, sans qu'il soit nécessaire de les modifier. val o var . Le traitement de ces paramètres dépend de leur utilisation dans la classe.

  • Si un paramètre sans val o var est utilisé dans au moins une méthode, il devient un champ. Par exemple,

     class Person(name: String, age: Int) {  
       def description = name + " is " + age + " years old"
     }

    déclare et initialise des champs immuables name y age qui sont privés d'objet. Un tel champ est l'équivalent d'un private[this] val (voir section 5.4, "Champs privés d'objets", à la page 56).

  • Dans le cas contraire, le paramètre n'est pas enregistré en tant que champ. C'est juste un paramètre normal auquel on peut accéder dans le code du constructeur primaire. (À proprement parler, il s'agit d'une optimisation spécifique à l'implémentation).

En fait, dans la version 2.13.2, je ne peux pas confirmer le deuxième cas.


Nous avons maintenant deux classes.

Scala n'autorise pas

class Superclass {
  val i: Int = 1
}

class Subclass extends Superclass {
  //val i: Int = 2 //doesn't compile
}

à moins que nous n'ajoutions override

class Superclass {
  val i: Int = 1
}

class Subclass extends Superclass {
  override val i: Int = 2
}

Mais si Superclass Le champ d'application de la loi est private[this] tout va bien sans override

class Superclass {
  private[this] val i: Int = 1
}

class Subclass extends Superclass {
  val i: Int = 2
}

En fait, si nous essayons d'ajouter override cela ne se compilera pas.

La raison en est qu'il n'est pas prioritaire. L'un des champs est private[this] c'est-à-dire qu'il n'est pas accessible en dehors de l'objet dans lequel le champ est défini, il s'agit donc de deux champs différents :

class Superclass {
  private[this] val i: Int = 1
}

class Subclass extends Superclass {
  val i: Int = 2

  println(this.i) // or just println(i)
// println((this: Superclass).i) //doesn't compile
}

new Subclass
//2

o

class Superclass {
  val i: Int = 1
}

class Subclass extends Superclass {
  private[this] val i: Int = 2

  println(this.i) // or just println(i)
  println((this: Superclass).i)
}

new Subclass
//2
//1

Ainsi, dans notre cas

class Superclass(var i : Int)

class Subclass(i : Int) extends Superclass(0)

sont comme

class Superclass extends AnyRef {
  var i: Int = _
  def this(_i: Int) = {
    super() //pseudocode
    i = _i
  }
}

class Subclass extends Superclass {
  private[this] val i: Int = _ //pseudocode
  def this(_i: Int) = {
    super(0) //pseudocode
    i = _i  //pseudocode because "i" is a val -- well, compiler can do things that we can't do in source code
  }
}

A l'intérieur Subclass this.i ou simplement i se réfère à Subclass Le domaine de la private[this] val i: Int y (this: Superclass).i se réfère à Superclass Le domaine de la var i: Int .


Est-ce que les paramètres des constructeurs scala prennent par défaut la valeur private ?

Paramètres du constructeur Scala

https://www.scala-lang.org/old/node/8384.html

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