为什么Java禁止内部类中的静态字段?

Jic*_*hao 79 java static-members inner-classes

class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 
Run Code Online (Sandbox Code Playgroud)

虽然无法访问静态字段OuterClass.InnerClass.i,但如果我想记录一些应该是静态的东西,例如创建的InnerClass对象的数量,那么将该字段设置为静态会很有帮助.那么为什么 Java禁止内部类中的静态字段/方法呢?

编辑:我知道如何使编译器满意静态嵌套类(或静态内部类),但我想知道的是为什么java禁止语言设计和内部类(或普通内部类)内的静态字段/方法实施方面,如果有人了解更多.

Osc*_*Ryz 45

我想知道的是为什么java禁止内部类中的静态字段/方法

因为那些内部类是"实例"内部类.也就是说,它们就像封闭对象的实例属性.

因为它们是"实例"类,所以允许static功能没有任何意义,因为static它首先在没有实例的情况下工作.

就像您尝试同时创建静态/实例属性一样.

请看以下示例:

class Employee {
    public String name;
}
Run Code Online (Sandbox Code Playgroud)

如果您创建两个员工实例:

Employee a = new Employee(); 
a.name = "Oscar";

Employee b = new Employee();
b.name = "jcyang";
Run Code Online (Sandbox Code Playgroud)

很清楚为什么每个人都有自己的财产价值name,对吗?

内部阶级也是如此; 每个内部类实例独立于另一个内部类实例.

因此,如果您尝试创建counter类属性,则无法在两个不同的实例之间共享该值.

class Employee {
    public String name;
    class InnerData {
        static count; // ??? count of which ? a or b? 
     }
}
Run Code Online (Sandbox Code Playgroud)

在创建实例时a,b在上面的示例中,静态变量的正确值是count什么?无法确定它,因为InnerData类的存在完全取决于每个封闭对象.

这就是为什么,当这个类被声明为static,它不再需要一个活生生的实例来生存.既然没有依赖关系,您可以自由声明静态属性.

我认为这听起来很反复,但如果你考虑实例与类属性之间的差异,它就会有意义.

  • 我将为内部类的静态*属性*购买你的解释,但正如@skaffman在对我的答案的评论中指出的那样,静态*方法*怎么样?似乎应该允许方法而不强制它们与任何实例分离.实际上,在java中,您可以在实例上调用静态方法(尽管它被认为是不好的样式).顺便说一句:我请一位同事尝试将OP的代码编译成C#,然后*编译.所以C#显然允许这表明OP想要做的事情并没有违反一些基本的OO原则. (4认同)
  • 与方法完全相同.这里的要点不是*属性*或*方法*是统计与否,而是内部类本身就是**实例**"东西".我的意思是,这里的问题是在创建外部类之前,这样的内部类的实例不存在.因此,如果什么都没有,那么将调度什么方法.你只会因为首先需要一个实例而打空. (2认同)
  • @OscarRyz为什么你需要内部类的实例来使用它的*static*methods/fields?内部类*中[有用]静态方法的一个例子是*private helper方法. (2认同)

Asa*_*aph 33

InnerClass不能拥有static成员,因为它属于一个实例(of OuterClass).如果声明InnerClassstatic从实例中分离它,你的代码进行编译.

class OuterClass {
    static class InnerClass {
        static int i = 100; // no compile error
        static void f() { } // no compile error
    }
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句:你仍然可以创建实例InnerClass.static在这种情况下允许在没有封闭实例的情况下发生OuterClass.

  • `InnerClass`确实*不属于`OuterClass`,*的实例*.这两个班级本身没有这种关系.为什么你不能在`InnerClass`中拥有静态方法的问题仍然存在. (6认同)
  • 亚萨,答案中没有答案.只是一个解决方法. (4认同)

Gre*_*osz 27

内部类背后的想法是在封闭实例的上下文中操作.不知何故,允许静态变量和方法与这种动机相矛盾?

8.1.2内部类和封闭实例

内部类是未显式或隐式声明为静态的嵌套类.内部类可能不会声明静态初始化器(第8.7节)或成员接口.内部类可能不会声明静态成员,除非它们是编译时常量字段(第15.28节).

  • 也许它只是**决定**那样 (17认同)
  • 从 Java 16 开始,情况不再如此 - 请参阅[此答案](/sf/answers/4560336301/)。 (4认同)
  • 如果没有父引用,你不能*实例化*非静态内部,但你仍然可以*初始化*它. (3认同)

Nic*_*lai 11

从 Java 16 开始,情况不再如此。引用JEP 395(关于最终记录):

放宽长期存在的限制,即内部类不能声明显式或隐式静态成员。这将变得合法,特别是将允许内部类声明作为记录类的成员。

确实,下面的代码可以用Java 16编译(用16.ea.27试过):

public class NestingClasses {

    public class NestedClass {

        static final String CONSTANT = new String(
                "DOES NOT COMPILE WITH JAVA <16");

        static String constant() {
            return CONSTANT;
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

  • @Lii因为Java长期以来一直允许在非静态嵌套类中使用静态常量变量,例如字符串文字。使用 `CONSTANT = "..."`,在 Java &lt; 15 上不会出现编译错误。 (5认同)
  • 更多信息可以在[JDK-8254321](https://bugs.openjdk.java.net/browse/JDK-8254321)中找到。 (3认同)

小智 9

实际上,如果静态字段是常量并且在编译时写入,则可以声明静态字段.

class OuterClass {
    void foo() {
        class Inner{
            static final int a = 5; // fine
            static final String s = "hello"; // fine
            static final Object o = new Object(); // compile error, because cannot be written during compilation
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这是我发现最适合此“限制”的动机:您可以将内部类的静态字段的行为实现为外部对象的实例字段; 因此,您不需要静态字段/方法。我的意思是,某个对象的所有内部类实例共享一个字段(或方法)。

因此,假设您想计算所有内部类实例,则可以这样做:

public class Outer{
    int nofInner; //this will count the inner class 
                  //instances of this (Outer)object
                  //(you know, they "belong" to an object)
    static int totalNofInner; //this will count all 
                              //inner class instances of all Outer objects
    class Inner {
        public Inner(){
            nofInner++;
            totalNofInner++;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 但是问题是,当将静态字段声明为“最终”时,允许静态字段的原因是什么? (2认同)

Sai*_*aif 5

  1. class初始化顺序 是一个关键原因。

由于内部类取决于封闭类/外部类的实例,因此需要在初始化内部类之前初始化外部类。
这是JLS关于类初始化的说明。 我们需要指出的是,如果

  • 使用由T声明的静态字段,并且该字段不是常量。

因此,如果内部类具有静态字段访问权限,这将导致初始化内部类,但不能确保封闭类被初始化。

  1. 这将违反一些基本规则您可以跳到最后一部分,two cases以避免出现菜鸟的玩意

关于的事情,有的时候,它的行为在各个方面都像普通的类,并且与Outer类相关联。static nested classnested classstatic

但是Inner class/ 的概念将与外部/封闭类相关联。请注意与实例关联的不是类。现在,与实例关联显然意味着(从实例变量的概念出发)它将存在于实例内部,并且在实例之间将有所不同。 non-static nested classinstance

现在,当我们使某些东西静态时,我们期望它会在加载类时被初始化,并且应该在所有实例之间共享。但是由于是非静态的,即使内部类本身(您现在肯定可以忘记内部类的实例)也没有与外部/封闭类的所有实例共享(至少在概念上),那么我们如何期望某个变量内部类的所有实例将在内部类的所有实例之间共享。

因此,如果Java允许我们在静态嵌套类中使用静态变量。将有两种情况

  • 如果它与内部类的所有实例共享,则将违反context of instance(实例变量)的概念。那是不。
  • 如果未与所有实例共享它,则将违反静态概念。再次没有。