Имена параметров конструктора и имена членов класса

#scala

#scala

Вопрос:

В целом я понимаю, как Scala работает с параметрами конструктора, то есть генерирует getter для val и getter / setter для var. Я также нашел довольно много обсуждений о столкновениях имен между параметрами конструктора и членами класса.

Для этих имен без val amp; var я ожидаю, что если они имеют префикс с this. , то вместо этого следует использовать члены класса. Но это не так.

 class A(val a: Int) {
  def m = println("A.a: "   a)
}

class B(a: Int) extends A(2 * a) {
  override def m = {
    super.m
    println("@a: "   a)
    println("this.a: "   this.a)  // this is @a, not A.a
  }
}

object TestApp extends App {
    val b = new B(10)
    b.m
    println(b.a); // this is A.a
}
 

Вывод:

 A.a: 20
@a: 10
this.a: 10
20
 

Я чувствую, что это немного странно, но есть ли за этим дизайнерская причина?

Ответ №1:

Как вы написали в своем вопросе, Scala генерирует геттер для a val , поэтому, когда вы объявляете val параметр в A своем классе, у него фактически есть метод m и поле a , которые наследуются B классом. Но у вас также есть a параметр в вашем B классе, который удваивается и передается в качестве аргумента суперконструктору A . Чтобы понять, что происходит, включите опцию -Xprint:typer для вашего репозитория и вставьте свой код, например A , представленный следующим образом:

 class A extends scala.AnyRef {
  <paramaccessor> private[this] val a: Int = _;
  <stable> <accessor> <paramaccessor> def a: Int = A.this.a;
  def <init>(a: Int): A = {
    A.super.<init>();
    ()
  };
  def m: Unit = scala.this.Predef.println("A.a: ". (A.this.a))
}
 

Как вы можете видеть, Scala создала скрытое private[this] поле для вашего a параметра. И вот представление для B класса:

 class B extends A {
  <paramaccessor> private[this] val a: Int = _;
  def <init>(a: Int): B = {
    B.super.<init>(2.*(a));
    ()
  };
  override def m: Unit = {
    B.super.m;
    println("@a: ". (B.this.a));
    println("this.a: ". (this.a))
  }
}
 

Как вы можете видеть, Scala также определила private[this] поле для вашего a параметра в B классе. Но поскольку вы не пометили его как val значение, он не сгенерировал новый a геттер, который по -прежнему определяется как A.this.a , но не B.this.a , поэтому вы получаете r 20 при вызове b.a .

В качестве ответа я не думаю, что за этим стоит какой-то сложный дизайн, он выглядит логичным и вполне разумным. Если вы переименуете свой a параметр в B конструкторе класса и передадите его A , ничего не изменится.