Scala对象初始化

mak*_*aks 13 scala

我有一个对象:

object A {
 val init = println("Hello")
}
Run Code Online (Sandbox Code Playgroud)

我在一个特征中使用它:

trait SomeTratit {
  val a = A.init
}
Run Code Online (Sandbox Code Playgroud)

然后我在课堂上使用trait:

class SomeClass extends SomeTrait
Run Code Online (Sandbox Code Playgroud)

当我实例化SomeClass时,new SomeClass我希望"Hello"在控制台中看到,但是没有得到它.为什么?

此外,我希望在实例化多个对象时只看到一次"Hello",但在控制台中看不到任何"Hello"

las*_*and 6

我不知道这是否应该被视为一个错误,但这是如何发生的。

如果你查看生成的字节码object A,它是这样的:

public final class A$ {
  public static final A$ MODULE$;

  private final scala.runtime.BoxedUnit init;

  public static {};
    Code:
       0: new           #2                  // class A$
       3: invokespecial #12                 // Method "<init>":()V
       6: return

  public void init();
    Code:
       0: return

  private A$();
    Code:
       0: aload_0
       1: invokespecial #16                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: putstatic     #18                 // Field MODULE$:LA$;
       8: aload_0
       9: getstatic     #23                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
      12: ldc           #25                 // String Hello
      14: invokevirtual #29                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
      17: getstatic     #34                 // Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
      20: putfield      #36                 // Field init:Lscala/runtime/BoxedUnit;
      23: return
}
Run Code Online (Sandbox Code Playgroud)

您可以println("Hello")在 的构造函数中找到您所期望的A$,但在init(). 这是完全正确的,因为您的意图println("Hello")是每次调用时都不会执行init(),对吗?

然而,问题来了SomeClass

public class SomeClass implements SomeTrait {
  public void a();
    Code:
       0: return

  public void SomeTrait$_setter_$a_$eq(scala.runtime.BoxedUnit);
    Code:
       0: return

  public SomeClass();
    Code:
       0: aload_0
       1: invokespecial #21                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: invokestatic  #27                 // Method SomeTrait$class.$init$:(LSomeTrait;)V
       8: return
}
Run Code Online (Sandbox Code Playgroud)

什么?!里面也什么都没有SomeClass.a()!但想想看,这也是完全合理的:我为什么要费心调用它,因为实际上什么都没有A$.init(),它也不返回任何内容(即没有要设置的字段)?为什么不直接优化它(也许 Java 做了这个优化。或者 Scala 做了。我不知道)?但是,这种优化也消除了 的唯一外观A$,这意味着不会调用 构造函数A$。这就是为什么Hello从来没有出现过。

但是,如果您稍微更改代码,使 的字节码init()不会为空,如下所示:

object A {
  val init = { println("Hello"); 1 }
}
Run Code Online (Sandbox Code Playgroud)

编译为以下字节码:

public int init();
  Code:
     0: aload_0
     1: getfield      #17                 // Field init:I
     4: ireturn
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你会找到这样的字节码SomeClass.a()

public int a();
  Code:
     0: aload_0
     1: getfield      #15                 // Field a:I
     4: ireturn
Run Code Online (Sandbox Code Playgroud)

该字段的设置位置SomeTrait$class

public abstract class SomeTrait$class {
  public static void $init$(SomeTrait);
    Code:
       0: aload_0
       1: getstatic     #13                 // Field A$.MODULE$:LA$;
       4: invokevirtual #17                 // Method A$.init:()I
       7: invokeinterface #23,  2           // InterfaceMethod SomeTrait.SomeTrait$_setter_$a_$eq:(I)V
       12: return
}
Run Code Online (Sandbox Code Playgroud)

A$.init()被调用来设置这个字段,所以在这种情况下你可以期待Hello出现。


Miu*_*ler -4

init 是 Unit 类型,因此评估该函数是徒劳的。