为什么阴影会影响“最终”行为?
Why does shadowing affect `final` behavior?
这里有三个 SSCCE,我认为它们的编译和行为应该相同。我唯一要更改的是 "run".
的第一行
图表 1
public class FinalExperiment {
private TinyThing thing;
public static void main(String[] args) {
FinalExperiment instance = new FinalExperiment();
instance.run();
}
public void run() {
final TinyThing thing = new TinyThing();
System.out.println("Got a thing here: " + thing);
}
private static class TinyThing {
public TinyThing() {}
public String toString() { return "Hello!"; }
}
}
这行得通;它编译成功,并打印:"Got a thing here: Hello!"
图表 2
public class FinalExperiment {
private TinyThing thing;
public static void main(String[] args) {
FinalExperiment instance = new FinalExperiment();
instance.run();
}
public void run() {
final TinyThing otherThing = thing;
System.out.println("Got a thing here: " + otherThing);
}
private static class TinyThing {
public TinyThing() {}
public String toString() { return "Hello!"; }
}
}
这行得通;它编译成功,并打印:"Got a thing here: null"
图表 3
public class FinalExperiment {
private TinyThing thing;
public static void main(String[] args) {
FinalExperiment instance = new FinalExperiment();
instance.run();
}
public void run() {
final TinyThing thing = thing;
System.out.println("Got a thing here: " + thing);
}
private static class TinyThing {
public TinyThing() {}
public String toString() { return "Hello!"; }
}
}
编译失败并显示以下消息:
FinalExperiment.java:10: error: variable thing might not have been initialized
final TinyThing thing = thing;
^
1 error
为什么?图表 2 和图表 3 之间的唯一区别是我在 run
方法中隐藏了 thing
。编译器似乎不应该更关心,因为正在发生阴影。
是的,图表 3 中出现了阴影,但实际上您是在尝试声明一个 final
变量,然后将其分配给自身。
final TinyThing thing = thing; // Assign to self!
它还没有被赋值,所以你会得到它没有被初始化的编译器错误。无论局部变量 thing
是否为 final
,都会发生这种情况。
要引用实例变量,用this
限定它。
final TinyThing thing = this.thing; // Bypass shadowing.
这编译并导致与图表 2 相同的输出:
Got a thing here: null
此示例无法以相同方式编译,例如:
public class SelfDefineExample {
public static void main(String[] args) {
int i = i;
}
}
在执行 final TinyThing thing = thing
时,您引用了尚未初始化的自身变量。
您可以通过 final TinyThing thing = this.thing;
解决这个问题
哪个会输出
Got a thing here: null
请注意,final
关键字不会改变此处的任何行为。
这与阴影无关,您可以使用任何其他值进行测试。
例如:Object a = a
会给出同样的错误。
变量在它们自己定义的范围内。这个:
int i = i;
尝试用自己的值初始化一个变量。但是它没有值,因为它还没有被初始化!因此错误。
这里有三个 SSCCE,我认为它们的编译和行为应该相同。我唯一要更改的是 "run".
的第一行图表 1
public class FinalExperiment {
private TinyThing thing;
public static void main(String[] args) {
FinalExperiment instance = new FinalExperiment();
instance.run();
}
public void run() {
final TinyThing thing = new TinyThing();
System.out.println("Got a thing here: " + thing);
}
private static class TinyThing {
public TinyThing() {}
public String toString() { return "Hello!"; }
}
}
这行得通;它编译成功,并打印:"Got a thing here: Hello!"
图表 2
public class FinalExperiment {
private TinyThing thing;
public static void main(String[] args) {
FinalExperiment instance = new FinalExperiment();
instance.run();
}
public void run() {
final TinyThing otherThing = thing;
System.out.println("Got a thing here: " + otherThing);
}
private static class TinyThing {
public TinyThing() {}
public String toString() { return "Hello!"; }
}
}
这行得通;它编译成功,并打印:"Got a thing here: null"
图表 3
public class FinalExperiment {
private TinyThing thing;
public static void main(String[] args) {
FinalExperiment instance = new FinalExperiment();
instance.run();
}
public void run() {
final TinyThing thing = thing;
System.out.println("Got a thing here: " + thing);
}
private static class TinyThing {
public TinyThing() {}
public String toString() { return "Hello!"; }
}
}
编译失败并显示以下消息:
FinalExperiment.java:10: error: variable thing might not have been initialized
final TinyThing thing = thing;
^
1 error
为什么?图表 2 和图表 3 之间的唯一区别是我在 run
方法中隐藏了 thing
。编译器似乎不应该更关心,因为正在发生阴影。
是的,图表 3 中出现了阴影,但实际上您是在尝试声明一个 final
变量,然后将其分配给自身。
final TinyThing thing = thing; // Assign to self!
它还没有被赋值,所以你会得到它没有被初始化的编译器错误。无论局部变量 thing
是否为 final
,都会发生这种情况。
要引用实例变量,用this
限定它。
final TinyThing thing = this.thing; // Bypass shadowing.
这编译并导致与图表 2 相同的输出:
Got a thing here: null
此示例无法以相同方式编译,例如:
public class SelfDefineExample {
public static void main(String[] args) {
int i = i;
}
}
在执行 final TinyThing thing = thing
时,您引用了尚未初始化的自身变量。
您可以通过 final TinyThing thing = this.thing;
哪个会输出
Got a thing here: null
请注意,final
关键字不会改变此处的任何行为。
这与阴影无关,您可以使用任何其他值进行测试。
例如:Object a = a
会给出同样的错误。
变量在它们自己定义的范围内。这个:
int i = i;
尝试用自己的值初始化一个变量。但是它没有值,因为它还没有被初始化!因此错误。