Scala 对象初始化 [英] Scala object initialization
问题描述
我有一个对象:
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屋!