Question about initializing an abstract type class in scala

I define a class with an abstract type as follows:

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} } 

Now I instantiate an object of type Int

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

Pay attention to the output from println . It seams the variable init not initialized as 10. Note that the scala version is 2.9.0-1

+4
source share
3 answers

I think you're looking for Scala early initializers ,

 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 

Early initializers let you assign a new object and set some specific fields before the class constructor runs. In this case, since value depends on init , we use the early initializer syntax ( new { val init = ... } with AbsCell2 ) to set init first so that the class constructor can correctly initialize value .

See also this question: In Scala, what is an “early initializer”?

+4
source

Change the value of val to 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} } 

It works as expected:

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

For the question of the difference between new { type T = Int ; val init = 10 } new { type T = Int ; val init = 10 } with AbsCell2 and new AbsCell2 { type T = Int ; val init = 10 } new AbsCell2 { type T = Int ; val init = 10 } .

The first is the so-called early initializers or pre-initialized fields (mentioned in the chapter “Abstract members” in “Scala Programming”). It allows the subclass to initialize the fields before calling the superclass. In this case, init was already set as 10 before AbsCell2 was initialized.

The latter is ordinary inheritance, it creates an anonymous class, and then extends AbsCell2, as:

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

However, the anonymous class is initialized after the abstract class AbsCell2, so init is not available in the initialization of itself, namely: init is the default value for Type, 0 in this case. Therefore you get 0 in print. A.

use scalac -Xprint:all Test.scala , you will see the following:

  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>(); () } } 

From the code we can see that for normal initialization, init has a value of 3 after the superclass of AbsCell2 is initialized, when AbsCell2.this.init () is called, it actually refers to the initialization field of the subclass and 3 is not set yet, so we get the default value of the type. In contrast, early initialization first sets up init 3 and then causes the class to be superclassed.

0
source

All Articles