【发布时间】:2015-09-08 17:13:46
【问题描述】:
我想了解我在处理匿名类时遇到的一种奇怪行为。
我有一个类在其构造函数中调用受保护的方法(我知道,糟糕的设计,但这是另一回事......)
public class A {
public A() {
init();
}
protected void init() {}
}
然后我有另一个类扩展 A 并覆盖 init()。
public class B extends A {
int value;
public B(int i) {
value = i;
}
protected void init() {
System.out.println("value="+value);
}
}
如果我编码
B b = new B(10);
我明白了
> value=0
这是意料之中的,因为超类的构造函数在 B ctor 之前调用,然后 value 仍然存在。
但是当使用这样的匿名类时
class C {
public static void main (String[] args) {
final int avalue = Integer.parsetInt(args[0]);
A a = new A() {
void init() { System.out.println("value="+avalue); }
}
}
}
我希望得到value=0,因为这应该或多或少等于类B:编译器会自动创建一个新类C$1,它扩展A,并创建实例变量来存储引用的局部变量匿名类的方法,模拟闭包等...
但是当你运行这个时,我得到了
> java -cp . C 42
> value=42
最初我认为这是因为我使用的是 java 8,也许在引入 lamdbas 时,他们改变了匿名类在后台实现的方式(您不再需要 final),但我也尝试了 java 7 并得到了相同的结果......
其实用javap看字节码,可以看出B是
> javap -c B
Compiled from "B.java"
public class B extends A {
int value;
public B(int);
Code:
0: aload_0
1: invokespecial #1 // Method A."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field value:I
9: return
...
而对于C$1:
> javap -c C\$1
Compiled from "C.java"
final class C$1 extends A {
final int val$v;
C$1(int);
Code:
0: aload_0
1: iload_1
2: putfield #1 // Field val$v:I
5: aload_0
6: invokespecial #2 // Method A."<init>":()V
9: return
....
谁能告诉我为什么会有这种差异? 有没有办法使用“普通”类来复制匿名类的行为?
编辑:
澄清问题:为什么匿名类的初始化会破坏任何其他类的初始化规则(在设置任何其他变量之前调用超级构造函数)?
或者,有没有办法在调用超级构造函数之前在B 类中设置实例变量?
【问题讨论】:
-
为什么你认为你的第一个和第二个代码是一样的?在第二个代码中,您正在访问局部变量。这将在您的匿名类语句执行之前被初始化。
-
嗯...好吧,你说:编译器创建一个类来实现这个场景的事实应该对开发人员隐藏,所以
C$1类是一个特例,如果它不遵循标准的构造函数规则。这是相当合理的,但是,恕我直言,这有点尴尬。
标签: java constructor javac anonymous-class javap