Scala 对象初始化 [英] Scala object initialization

查看:73
本文介绍了Scala 对象初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个对象:

object A {
 val init = println("Hello")
}

我在特征中使用它:

trait SomeTratit {
  val a = A.init
}

然后我在类中使用 trait it:

Then I use trait it in a class:

class SomeClass extends SomeTrait

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

When I instantiate SomeClass with new SomeClass I expect to see "Hello" in the console, but don't get it. Why?

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

Also I expect to see "Hello" only once while instantiating several objects, but don't see any "Hello" in console

推荐答案

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

I don't know if this should be considered a bug, but here's HOW this happened.

如果看一下object A生成的字节码,是这样的:

If you look at the generated bytecode of object A, it's like this:

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
}

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

You can find the println("Hello") you expected in the constructor of A$, but there's nothing in init(). This is perfectly correct because it is you intention that println("Hello") would not be executed every time when you called init(), right?

然而,问题出在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
}

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

What?! There's nothing in SomeClass.a(), either! But think about it, it's also perfectly reasonable: why would I bother calling it since there's literally nothing in A$.init() and it returns nothing (i.e. no field to be set)? Why not just optimize that out (Maybe Java did this optimization. Or Scala did. I don't know)? However, this optimization also erase the only appearance of A$, which means no constructor will be called for A$. That's why the Hello never appeared.

但是,如果你稍微修改一下代码,使得init()的字节码不会为空,就像这样:

However, if you change the code a little bit so that the bytecode of init() will not be empty, like this:

object A {
  val init = { println("Hello"); 1 }
}

编译成以下字节码:

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

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

in which case you will find the bytecode for SomeClass.a() like this:

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

字段在 SomeTrait$class 中设置的位置:

where the field was set in 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
}

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

A$.init() is called to set this field, so in this case you can expect Hello to appear.

这篇关于Scala 对象初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆