Вопрос по инициализации класса абстрактного типа в scala

Я определяю класс с абстрактным типом следующим образом:

abstract class AbsCell2{
    type T
    val init: T
    private var value: T = {
         println("Hello "+init);
         init
    }
    def get : T = value
    def set(x : T) = { value = x}
}

Теперь я создаю экземпляр объекта с типом Int

scala> val cell = new AbsCell2{type T = Int ; val init = 10}
Hello 0
cell: AbsCell2{type T = Int} = $anon$1@969ebb

Обратите внимание на вывод из println, Это швы переменной init не был инициализирован как 10. Обратите внимание, что версия scala - 2.9.0-1.

3 ответа

Решение

Я думаю, что вы ищете ранние инициализаторы Scala,

scala> val cell = new {type T = Int ; val init = 10} with AbsCell2
Hello 10
cell: AbsCell2{val init: Int; type T = Int} = $anon$1@1efa9557

scala> cell.get
res0: cell.T = 10

Ранние инициализаторы позволяют вам выделить новый объект и задать некоторые конкретные поля перед запуском конструктора класса. В этом случае, так как value зависит от initмы используем ранний синтаксис инициализатора (new { val init = ... } with AbsCell2) к первому набору init так что конструктор класса может правильно инициализировать value,

Смотрите также этот вопрос: что такое "ранний инициализатор" в Scala?

Изменить с val на def:

abstract class AbsCell2{
  type T
  def init: T
  private var value: T = {
     println("Hello "+init);
     init
  }
  def get : T = value
  def set(x : T) = { value = x}
}

Работает как положено:

scala> val cell = new AbsCell2{type T = Int ; def init = 10}
Hello 10
cell: AbsCell2{type T = Int} = $anon$1@4302df5

На вопрос о разнице между new { type T = Int ; val init = 10 } с AbsCell2 и new AbsCell2 { type T = Int ; val init = 10 },

Первый - это так называемые ранние инициализаторы или предварительно инициализированные поля (упомянутые в главе "Абстрактные члены" в Программирование в Scala). Это позволяет подклассу инициализировать поля до вызова суперкласса. В этом случае init уже был установить как 10 до инициализации AbsCell2。

Последний является нормальным наследованием, он создает анонимный класс, затем расширяет AbsCell2, как:

 class AbsCell' extends AbsCell {
    type T = Int 
    val init = 10 
 }

Однако анонимный класс инициализируется после абстрактного класса AbsCell2, поэтому init не доступны при самой инициализации, а именно init является значением по умолчанию Type, в данном случае 0. Таким образом, вы получаете 0 в печати。

использование scalac -Xprint:all Test.scala, вы увидите следующее:

 abstract class AbsCell2 extends Object {
    <stable> <accessor> def init(): Object;
    ....
   def <init>(): AbsCell2 = {
    AbsCell2.super.<init>();
    AbsCell2.this.value = {
      scala.this.Predef.println("Hello ".+(AbsCell2.this.init()));
      AbsCell2.this.init()
    };
    ()
   }
 };

 // normal initialization
 final class anon$1 extends AbsCell2 {
    private[this] val init: Int = _;
    <stable> <accessor> def init(): Int = anon$1.this.init;
    ....
    def <init>(): <$anon: AbsCell2> = {
     anon$1.super.<init>();
     anon$1.this.init = 10;
     ()
   }
 };

 // early initialization
 final class anon$2 extends AbsCell2 {
   private[this] val init: Int = _;
   <stable> <accessor> def init(): Int = anon$2.this.init;
   ....
   def <init>(): <$anon: AbsCell2> = {
     val init: Int = 10;
     anon$2.this.init = init;
     anon$2.super.<init>();
     ()
   }
 }

Из кода видно, что для нормальной инициализации init устанавливается в 3 после инициализации суперкласса AbsCell2, когда вызывается AbsCell2.this.init(), он фактически ссылается на поле инициализации подкласса, а 3 еще предстоит установить, так что мы получаем значение типа по умолчанию early Напротив, для ранней инициализации сначала установите init в 3, а затем вызовите инициализацию суперкласса。

Другие вопросы по тегам