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

时间:2009-12-23 15:46:04

标签: java inner-classes static-members

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

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

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

10 个答案:

答案 0 :(得分:43)

  

我想知道的是为什么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时,它不再需要一个活生生的实例来生存。既然没有依赖关系,你可以自由地声明一个静态属性。

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

答案 1 :(得分:33)

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

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

BTW:您仍然可以创建InnerClass的实例。在此上下文中static允许在没有OuterClass的封闭实例的情况下发生。

答案 2 :(得分:26)

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

  

8.1.2内部类和封闭实例

     

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

答案 3 :(得分:8)

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

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

答案 4 :(得分: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++;
        }
    }
}

答案 5 :(得分:5)

  1. 类初始化序列是一个重要原因。
  2. 由于内部类依赖于封闭/外部类的实例,因此需要在Inner类初始化之前初始化外部类。
    This is JLS says about class Initialization.我们需要的是,如果

    ,则T类将被初始化
      
        
    • 使用由T声明的静态字段,该字段不是常量变量。
    •   

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

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

    2. 关于 static nested class的一件事,当某些nested class static 时,它的行为就像一个各种方式的普通类,它与外类相关联。

      Inner class / non-static nested class的概念是与 instance 相关联外/封闭类。请注意与实例相关联,而不是类。 现在与实例关联清楚意味着(来自实例变量的概念)它将存在于实例中并且在实例之间将是不同的。

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

      因此,如果Java允许我们在静态嵌套类中使用静态变量。会有两个案例

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

答案 6 :(得分:3)

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

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

答案 7 :(得分:1)

因为这会引起“静态”含义的歧义。

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

摘自Cay S. Horstmann撰写的“不耐烦的Core Java SE 9”。 Pg 90第2.6.3章

答案 8 :(得分:0)

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

放松长期以来的限制,即内部类不能声明显式或隐式静态的成员。这将变得合法,尤其是允许内部类声明一个属于记录类的成员。

实际上,以下代码可以使用Java 16编译(尝试使用16.ea.27):

nsgs_assocs = {
  my_nsg_1 = ["my_subnet_1"]
  my_nsg_2 = ["my_subnet_2"]
}

答案 9 :(得分:-1)

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

相关问题