我想了解一个我在处理匿名课程时遇到的奇怪行为.
我有一个类在其构造函数中调用受保护的方法(我知道,设计很差,但这是另一个故事…)
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之前被调用,然后值仍然是.
但是在使用像这样的匿名类时
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); }
}
}
}
我希望得到值= 0因为它应该或多或少等于B类:编译器自动创建一个新的类C $1,它扩展A并创建实例变量来存储匿名类的方法中引用的局部变量,模拟关闭等……
但是当你运行这个时,我得到了
> java -cp . C 42
> value=42
最初我认为这是因为我使用的是java 8,并且可能在引入lamdbas时,他们改变了匿名类在引擎盖下的实现方式(你不再需要最终版),但我尝试使用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."":()V
4: aload_0
5: iload_1
6: putfield #2 // Field value:I
9: return
...
而对于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."":()V
9: return
....
有人能告诉我为什么会有这种差异吗
有没有办法使用“普通”类复制匿名类的行为?
编辑:
澄清问题:为什么匿名类的初始化会破坏初始化任何其他类的规则(在设置任何其他变量之前调用超级构造函数)?
或者,有没有办法在inovking超级构造函数之前在B类中设置实例变量?