89
class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 

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

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

4

11 回答 11

57

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

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

static由于它们是“实例”类,因此允许功能没有任何意义,因为static首先意味着在没有实例的情况下工作。

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

举个例子:

class Employee {
    public String name;
}

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

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

Employee b = new Employee();
b.name = "jcyang";

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

内部类也是如此。每个内部类实例都独立于另一个内部类实例。

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

class Employee {
    public String name;
    class InnerData {
        static count; // ??? count of which ? a or b? 
     }
}

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

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

我认为这听起来很重复,但是如果您考虑实例与类属性之间的差异,那将是有道理的。

于 2009-12-23T17:17:28.177 回答
34

InnerClass不能有static成员,因为它属于一个实例 (of OuterClass)。如果您声明将其InnerClassstatic实例分离,您的代码将编译。

class OuterClass {
    static class InnerClass {
        static int i = 100; // no compile error
        static void f() { } // no compile error
    }
}

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

于 2009-12-23T15:52:29.050 回答
34

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

8.1.2 内部类和封闭实例

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

于 2009-12-23T15:53:42.200 回答
16

从 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;
        }

    }

}
于 2020-12-04T17:03:02.603 回答
10

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

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
        }
    }
}
于 2014-12-22T19:32:16.617 回答
8
  1. 类初始化顺序 是一个关键原因。

由于内部类依赖于封闭/外部类的实例,所以外部类需要在内部类初始化之前进行初始化。
这是 JLS 关于类初始化的说法。 我们需要的一点是,如果 T 类被初始化

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

因此,如果内部类有一个静态字段访问将导致初始化内部类,但这并不能确保封闭类已初始化。

  1. 这将违反一些基本规则你可以跳到最后一节(到two cases)以避免菜鸟的东西

关于 的一件事,当一些是时,它在各个方面的行为都像一个普通的类,并且它与 Outer 类相关联。static nested classnested classstatic

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

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

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

  • 如果它与内部类的所有实例共享,它将违反context of instance(实例变量)的概念。那是NO。
  • 如果它不与所有实例共享,它将违反静态的概念。再次没有。
于 2016-09-09T09:26:43.383 回答
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++;
        }
    }
}
于 2015-03-24T12:27:55.470 回答
2

简单来说,非静态内部类是外部类的实例变量,只有在创建外部类并在运行时创建外部类对象时才会创建它们,而静态变量是在类加载时创建的。所以非静态内部类是运行时的东西,这就是为什么静态不是非静态内部类的一部分。

注意:始终将内部类视为外部类的变量,它们可以像任何其他变量一样是静态或非静态的。

于 2017-12-15T07:45:37.187 回答
0

因为它会导致“静态”的含义产生歧义。

内部类不能声明除了编译时常量之外的静态成员。关于“静态”的含义会有歧义。这是否意味着虚拟机中只有一个实例?或者每个外部对象只有一个实例?语言设计者决定不解决这个问题。

摘自 Cay S. Horstmann 的“Core Java SE 9 for the Impatient”。第 90 页第 2.6.3 章

于 2020-05-05T02:40:32.547 回答
0

Java 语言设计者自己的话来说

自从嵌套类首次被引入 Java 以来,内部的嵌套类声明被禁止声明静态成员......它简化了语言解析和验证对范围内变量、方法等的引用的任务。

从来没有任何特别宏大的概念或哲学理由来禁止这一点。

简化语言的事情被认为不足以继续保持这种限制。随着 Java 16 中记录的引入,他们决定放宽限制。

于 2021-12-01T16:44:46.253 回答
-1

我想这是为了一致性。虽然它似乎没有任何技术限制,但您将无法从外部访问内部类的静态成员,即OuterClass.InnerClass.i因为中间步骤不是静态的。

于 2016-06-22T00:29:30.897 回答