为什么阴影会影响“最终”行为?

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;

尝试用自己的值初始化一个变量。但是它没有值,因为它还没有被初始化!因此错误。