为什么所有匿名类都隐式地是final的?

6
根据JLS:
15.9.5 匿名类声明:匿名类声明是由编译器根据类实例创建表达式自动推导出的。
匿名类从不是抽象类(§8.1.1.1)。匿名类始终是内部类(§8.1.3); 它永远不会是静态的(§8.1.1,§8.5.2)。匿名类始终是隐含 final 的(§8.1.1.2)。
这似乎是一项具体的设计决策,因此很可能有一些历史背景。
如果我选择像这样的类:
SomeType foo = new SomeType() {
    @Override
    void foo() {
        super.foo();
        System.out.println("Hello, world!");
    }
};

如果我选择这样做,为什么我不能再次子类化它?

SomeType foo = new SomeType() {
    @Override
    void foo() {
        super.foo();
        System.out.println("Hello, world!");
    }
} {
    @Override
    void foo() {
        System.out.println("Hahaha, no super foo for you!");
    }
};

我不是说我一定想要这么做,甚至也想不出为什么我会这样做。但我很好奇为什么会这样。


你会接受“语法不允许”吗?;-) 这可能是他们说“隐式 final”的原因...只是猜测。 - David Z
1
当编译时,它们不是匿名的也不是私有的。尽管它们仍然包含元数据,但将它们设置为final可以确保没有超级奇怪的情况(通过扩展它们)。 - bestsss
@David 不,我不会这样做。看起来这是一个故意的设计决定,不允许它。 - corsiKa
5
唯一的子类化匿名类的方法就是你在例子中展示的,但这种方式似乎没有什么用处。语言设计不仅仅关乎你添加哪些功能,还关乎你省略了哪些功能。我没有看到任何技术原因(即如果有人真的想要实现它,则可以实现),不去做这件事情,除非它增加了复杂性而没有任何好的使用案例。 - Voo
2
@Voo 语言设计至少与你放入的内容一样重要,也与你留下的内容一样重要。 - 是的,我认为几十年前像 Dijkstra 这样的人说过这句话,现在仍然是正确的。 - Hot Licks
显示剩余6条评论
1个回答

6

如果能够子类化匿名类的话,那么就会变得相当无用。唯一可以引用匿名类的地方是在定义语句中(就像你的假设伪代码示例所展示的那样)。这意味着程序保证不会创建任何匿名超类的实例,而聪明的编译器应该能够把两个定义合并成一个类。

更实际的情况是,在类为final时,编译器和虚拟机可以自由地将其方法内联到调用处。因此,在任何自然上无法扩展给定类的情况下,让这样的类本质上成为final是有意义的。


1
有时候在测试代码(模拟、间谍)中,子类化匿名类会很有帮助。 - steventrouble
无论它是否是final,如果你无法通过名称引用一个类,那么你就无法对其进行子类化(就像你无法对没有可访问构造函数的类进行子类化一样)。而最近的规范已经更改为“匿名类永远不会是final”(https://dev59.com/3lQJ5IYBdhLWcg3wW0my#54019398)... - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接