有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

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

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

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


共 (6) 个答案

  1. # 1 楼答案

    从Java16开始,情况就不再如此了。从JEP 395引用(关于记录定稿):

    Relax the longstanding restriction whereby an inner class cannot declare a member that is explicitly or implicitly static. This will become legal and, in particular, will allow an inner class to declare a member that is a record class.

    实际上,以下代码可以用Java16编译(使用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;
            }
    
        }
    
    }
    
  2. # 2 楼答案

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

    8.1.2 Inner Classes and Enclosing Instances

    An inner class is a nested class that is not explicitly or implicitly declared static. Inner classes may not declare static initializers (§8.7) or member interfaces. Inner classes may not declare static members, unless they are compile-time constant fields (§15.28).

  3. # 3 楼答案

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

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

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

  4. # 4 楼答案

    what I want to know is why java forbids static fields/methods inside inner classes

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

    因为它们是“实例”类,所以允许static特性没有任何意义,因为static本来就应该在没有实例的情况下工作

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

    以以下为例:

    class Employee {
        public String name;
    }
    

    如果创建两个employee实例:

    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时,它不再需要一个活实例来活自己。既然没有依赖项,就可以自由声明静态属性

    我认为这听起来是重复的,但是如果你考虑一下实例属性和类属性之间的区别,这是有意义的

  5. # 5 楼答案

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

    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
            }
        }
    }
    
  6. # 6 楼答案

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

    由于内部类依赖于封闭/外部类的实例,因此需要在初始化内部类之前初始化外部类
    This is JLS says about class Initialization.我们需要的是,如果

    • A static field declared by T is used and the field is not a constant variable.

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

    1. 这将违反一些基本规则您可以跳到最后一节(到two cases),以避免noob内容

    关于static nestedclass的一点是,当一些nested classstatic时,它的行为在各个方面都与普通类一样,并且与外部类相关联

    但是Inner class/{}{}的概念是它将与外部/封闭类的{}相关联。请注意与实例关联的不是类。 现在,与实例关联显然意味着(来自实例变量的概念)它将存在于实例内部,并且在实例之间会有所不同

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

    所以,如果Java允许我们在非静态嵌套类中使用静态变量。将有两个案例

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