为什么Java 16允许在非静态内部类中使用静态方法?

34

我们知道非静态内部类可以使用外部类实例进行访问,因此静态方法在非静态类内部的意义较小。但是从Java 16开始,在非静态内部类中允许出现静态方法。

为什么一开始存在这个限制?为什么在新版本中允许这样做呢?

public class OuterClass {

    class InnerClass {
        static void printMe() {
            System.out.println("Inside inner class");
        }
    }

    public static void main(String[] args) {
        InnerClass.printMe();
    }

}

14
为什么新版本允许这样做呢?可能是因为它很有用。我有时也希望能这样做。一个封装在适当位置且不依赖于任何实例(内部或外部)的方法似乎是明确定义的。 - iggy
37
更好的问题是:为什么一开始会有这样的限制存在? - Brian Goetz
5
@BrianGoetz,你能回答这个问题吗? :) - Andy Turner
@paulsm4 无论如何,我们都会为外部类创建一个实例,所以认为这是没有意义的。正如Brian Goetz所说,我想知道为什么一开始就存在这样的限制? - Pradeesh tet
3
@AndyTurner 我的印象是他们想要假装每个外部类的实例都有一个全新的内部类,就好像 outer1.InnerClass 是一个不同于 outer2.InnerClass 的类一样。当然,这并不是实际的工作方式,但通过禁止使用 static 关键字,他们可以让这种幻觉持续更长一些时间... - user253751
9
@AndyTurner 用户 user253751 的答案大多是正确的——在嵌套类被添加(Java 1.1)时,静态成员在另一个类中有多种可能的解释,因此这个问题被推迟了。 - Brian Goetz
2个回答

38
您正在询问Java 16中的更改原因,因此您应该首先查看发布说明,看看它是否有相关内容。 它是这样描述的:

JEP 395: Records (JDK-8246771)
tools/javac
Java语言中添加了Records功能。 Records是Java语言中新的一类类。 它们作为不可变数据的透明载体,比普通类具有更少的仪式感。

自从嵌套类第一次引入Java以来,除静态final字段初始化为常量表达式之外,内部嵌套类声明静态成员被禁止。 这个限制适用于非静态成员类、本地类和匿名类。

JEP 384: Records (Second Preview) 添加了对本地接口、枚举类和记录类的支持,它们都是静态定义。 这是一个受欢迎的增强,允许缩小某些声明的范围到本地上下文。

虽然JEP 384允许静态局部类和接口,但没有放宽内部类的静态成员类和接口的限制。内部类可以在其方法体中声明静态接口,但不能作为类成员。

作为自然的下一步,JEP 395进一步放松嵌套限制,并允许在内部类中声明静态类、方法、字段等。

更多信息请参见JEP 395


2
有趣的是,现在Java也加入了Records,在C#引入Records一年后... - csstudent1418

27
具体原因详见JEP 395

内部类的静态成员

当前规范中指定,如果内部类声明了一个明确或隐式地是静态的成员(除非该成员是一个常量变量),则会导致编译时错误。这意味着,例如,内部类不能声明一个记录类成员,因为嵌套记录类隐式地是静态的。

为了允许内部类声明明确或隐式地是静态的成员,我们放宽了这一限制。特别地,这允许内部类声明一个静态成员,该静态成员是一个记录类。

换句话说,有必要去除内部类的静态成员限制以解决特定情况,即允许在内部类中声明record类。但他们决定趁机去除所有情况下的限制。

这意味着设计者们得出结论,原始限制整体上既不是技术上必要的,也不是理想的。


为什么最初存在这种限制?

那是一个更加困难的问题。限制嵌套类中静态字段的决定应该在1996年或1997年早期设计Java 1.1时就已经做出了。除非有人能找到当时的书面资料,否则很难准确地记住原始决策背后的原因。所以我们永远不会确定。

(Brian Goetz在上面发表了评论:"... 在添加嵌套类(Java 1.1)时,静态字段在另一个类中具有多种可能的解释,因此这个问题被搁置了。"。这当然说得通,但这只是一个人对25年前发生的事情的回忆。如果是我,我不会对那么久之前的记忆感到自信,除非我有当时的记录、笔记等可以参考。)

关于最初限制的理由,有一些猜测:


3
在非静态上下文声明的类中禁止静态可变状态(即除常量外没有静态字段)的一些好的动机包括:(1) 它们不需要,因为它们等效于外部实例的字段,(2) 许多程序员会期望它们等同于外部类的静态字段,即全局的,从而导致混淆,(3) 静态初始化程序(和具有初始化程序表达式的静态字段)需要在每个实例创建后执行,而不仅仅是在加载类时执行一次,这会使类加载器复杂化。 - kaya3
1
这个无关紧要。重点是当前负责Java语言的人明显没有被这些理由所说服。 - Stephen C
是的,我只是在你最后一段的基础上提出了一些可能导致它在旧版Java中不被允许的原因。 - kaya3

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