2015-03-26 4 views
1

У меня есть абстрактный класс, который расширен другим классом.Абстрактный класс с абстрактными переменными и дочерними конструкторами; Scala

object StackOverflowTest extends App 
{ 
    new ChildFunction() 
} 

abstract class Function() { 
    val a: Double 
    val b: Double 

    println(a, b) 
} 

class ChildFunction() extends Function() { 
    val a = 0.02 
    val b = 0.2 
} 

Когда я создаю экземпляр ChildFunction, он печатает 0.0 для a и b.

Это, очевидно, не то поведение, которое я хочу, либо даже ожидал.

Единственный способ, которым я нашел исправление этой проблемы, указан в ChildFunctionlazy val a = 0.02. Это подходящее решение?

+1

Это поможет нам, если ваш пример будет завершен, чтобы мы могли его запустить. Что такое терминал и нейрональная функция, они даже необходимы для целей воспроизведения проблемы? –

+0

@ChrisK Вы абсолютно правы. Я очистил пример. – Andrey

ответ

2

Выполнение работы val lazy, поскольку оно перемещает точку, в которой 0.02 записывается в поле. Чтобы понять, что происходит, прочитайте следующий код, который представляет собой java-байтовые коды, которые производит scalac для вашего примера.

Следует отметить, что поля a и b сохраняются на дочернем объекте и что их значения 0.02 и 0.2 не записываются до тех пор, пока не вызывается конструктор родителя. Однако println вызывается в конструкторе родителя, прежде чем поля будут записаны. Таким образом, ваша проблема.

Выполнение работы vals lazy, поскольку в момент вызова a() или b() будет вызываться код инициализации .. то есть родительский класс теперь будет вызывать код в дочернем классе для установки полей на класс ребенка.

public abstract class Function implements scala.ScalaObject { 
    public abstract double a(); 

    public abstract double b(); 

    public Function(); // NB: calls a() and b() on the child class 
    Code: 
     0: aload_0  
     1: invokespecial #13     // Method java/lang/Object."<init>":()V 
     4: getstatic  #19     // Field scala/Predef$.MODULE$:Lscala/Predef$; 
     7: new   #21     // class scala/Tuple2$mcDD$sp 
     10: dup   
     11: aload_0  
     12: invokevirtual #25     // Method a:()D 
     15: aload_0  
     16: invokevirtual #27     // Method b:()D 
     19: invokespecial #30     // Method scala/Tuple2$mcDD$sp."<init>":(DD)V 
     22: invokevirtual #34     // Method scala/Predef$.println:(Ljava/lang/Object;)V 
     25: return   
} 



public class ChildFunction extends Function implements scala.ScalaObject { 
    public double a(); 
    Code: 
     0: aload_0  
     1: getfield  #12     // Field a:D 
     4: dreturn  

    public double b(); 
    Code: 
     0: aload_0  
     1: getfield  #14     // Field b:D 
     4: dreturn  

    public ChildFunction(); // NB invokes parent constructor BEFORE writing values to fields a and b. 
    Code: 
     0: aload_0  
     1: invokespecial #20     // Method Function."<init>":()V 
     4: aload_0  
     5: ldc2_w  #21     // double 0.02d 
     8: putfield  #12     // Field a:D 
     11: aload_0  
     12: ldc2_w  #23     // double 0.2d 
     15: putfield  #14     // Field b:D 
     18: return   
} 

Вы можете исправить эту проблему, используя defs вместо lazy val (пример ниже). Или еще лучше, удалите println и только вызовите a() и b() после ChildFunction был полностью построен.

object StackOverflowTest extends App 
{ 
    new ChildFunction() 
} 

abstract class Function() { 
    def a: Double 
    def b: Double 

    println(a, b) 
} 

class ChildFunction() extends Function() { 
    override def a = 0.02 
    override def b = 0.2 
} 
+0

У меня была подсказка, что что-то подобное происходит. Это может привести к многочисленным ошибкам. Есть ли способ убедиться, что 'a' и' b' загружены, не делая их ленивыми? – Andrey

+0

Используя defs, компилятор будет жестко задавать значения внутри методов. В противном случае я бы не стал размещать println внутри конструктора родителя и вместо этого называть его после того, как ChildFunction полностью инициализирован. –

Смежные вопросы