今天我偶然发现了一些奇怪的内部(非静态)类行为。
如果我有以下课程......
class B {
String val = "old";
void run(){
val = "new";
System.out.println(val); // outputs: new
new InnerB().printVal(); // outputs: new
}
private class InnerB {
void printVal(){ System.out.println(val); }
}
}
new B().run();
......一切似乎都很清楚。 InnerB的实例属于B的实例,因此如果它应该输出val,则打印已经替换的值' new'。
但是如果内部类扩展了外部类,那么它就无法工作。
class B {
String val = "old";
void run(){
val = "new";
System.out.println(val); // outputs: new
new InnerB().printVal(); // outputs: new
new InheritedB().printVal(); // outputs: old new
}
private class InnerB {
void printVal(){ System.out.println(val); }
}
private class InheritedB extends B{
void printVal(){ System.out.println(val + " "+ B.this.val); }
}
}
new B().run(); // outputs: new new old!
如果我查看构造函数,我还会看到如果创建了InheritedB的实例,将创建一个新的B实例。
我发现这很奇怪......有人能解释为什么会有这种差异吗?
答案 0 :(得分:26)
这一行:
new InheritedB().printVal();
创建一个InheritedB
的新实例,其实例是B
的现有实例(其中val为"new"
)。但此时有两个 val
变量:
B
InheritedB
实例中的一个,具有单独的val
字段第二个变量的值为"old"
,因为它实际上是该字段的默认值。
InheritedB
中的此声明:
System.out.println(val + " "+ B.this.val);
打印出从val
继承的B
的值,后跟“包含实例”中的val
值。
可能更容易想到它被重构为:
public class B
{
String val = "old";
}
public class InheritedB extends B {
B other;
public InheritedB(B other)
{
this.other = other;
}
void printVal() {
System.out.println(val + " "+ other.val);
}
}
然后你基本上跑了:
B original = new B();
original.val = "new":
InheritedB inherited = new InheritedB(original);
inherited.printVal();
希望你能完全按照那里发生的事情。编译器大致将原始代码执行到该代码中。
答案 1 :(得分:9)
val
中的{p> InheritedB
引用其基类(val
)中的super.val
,因为它是this
的一部分{ {1}}。
如果您不从外部类继承,val
引用外部类(B.this.scope
)的范围。但是,由于您继承,this
的范围更近,因此隐藏了外部范围。
由于您从未在内部run()
上调用this
,this.val
仍为old
。
如果我查看构造函数,我还会看到如果创建了InheritedB的实例,将创建一个新的B实例。
是;创建派生类将始终创建其基类的实例。无法从现有实例继承。
答案 2 :(得分:5)
从InheritedB extends B
开始,创建一个InheritedB实例会授予它val
属性,默认情况下,该属性对于任何新 B类或子类实例都是“旧”。
此处,InheritedB
打印自己的 val
属性,而不是打包的B实例。
答案 3 :(得分:4)
在InheritedB
的情况下,有两个名为val
的变量,B
和InheritedB
之一。应用可见性规则可以得出观察结果。
答案 4 :(得分:2)
不同之处是,班级InnerB
中没有成员val
。其中,类InheritedB
扩展了类B,并拥有自己的val
成员副本。
void run(){
val = "new"; //<--- modifies B's val not InheritedB's val
System.out.println(val); // outputs: new
new InnerB().printVal(); // outputs: new
new InheritedB().printVal(); // outputs: old new
}
在上面的代码块中,InnerB的printVal访问容器的val
成员,该值已在run
方法中修改为值 new 。
但是InheritedB的对象中的val副本仍然是“旧”值,未修改,printVal函数使用该值。