为什么内部类不能使用静态初始化器?

7

引用自JLS #8.1.3

内部类不能声明静态初始化器§8.7)......

示例如下:

class A {
    class B {
        static { // Compile-time Error: Cannot define static initializer in inner type A.B
            System.out.println("Class is initializing...");
        }
    }
}

现在由于Java的内部(非静态)类和其他类一样是通过类加载器加载的,那么为什么我们不能为它们定义静态初始化器呢?
这个限制背后的原因是什么?

在我看来,没有什么好的理由去这样做,只需在封闭类的静态初始化器中声明即可。这也可能意味着它是实例绑定的吗? - user2982130
@xTrollxDudex,外部类的静态初始化器中的代码将在加载外部类时运行,即使内部类尚未加载。允许我们在内部类中拥有静态初始化器意味着我们可以延迟加载内部类的初始化代码,这是一件好事。 - Pacerier
3个回答

0

我认为这是因为内部类本身是非静态的。从Java的角度来看,它是一个实例变量,我想(1)类加载器没有设计成可以遍历内部非静态类来查找和初始化潜在的静态对象。

但这并不是不可能的问题,请看以下示例:

public class Outer {
    public static class Inner {
        Outer owner;
        static String constant;

        {
            constant = "foo";
        }

        private Inner(Outer owner) {
            if (owner == null) {
                throw new NullPointerException();
            }
            this.owner = owner;
        }
    }

    public Inner newInner() {
        return new Inner(this);
    }
}

因为Inner被声明为静态,所以甚至没有警告。

但仔细观察后,它有一个指向外部Outer实例的指针,只能通过Outer创建,因为它只有一个私有构造函数,并且其所有者不能为null。从程序员的角度来看,它具备非静态内部类的所有约束条件,并可以像非静态内部类一样使用(除了特殊的习惯用法Outer.this),但从编译器的角度来看,它是静态的,并且它的静态字段将在首次初始化Outer类时正确初始化。

(1):Pacerier在下面的评论中解释了为什么这是不正确的。


你说限制的原因是因为“从Java的角度来看,它[内部类]是一个实例变量”。这是不正确的。内部类是一个独立的类,可以通过类加载器加载而无需先加载外部类。这可以通过在java中使用-verbose标志并执行以下代码进行验证:Class.forName("Outer$Inner"); Class.forName("Outer");。我们可以看到输出显示“已加载Outer$Inner”之前显示“已加载Outer”。 - Pacerier

0

无效的用途

这只是我想到的一个观点,欢迎讨论和辩论

请阅读下面的主题。

这解释了为什么Java禁止在内部类中使用静态字段

在我看来,同样的原因也适用于static initializer。毕竟,造成问题的是关键字 static

除了上述主题中解释的原因外,我还可以给出另一个不恰当的理由
块的名称static initializer向我们提示何时以及为什么使用此块。一个人不可能只是使用静态初始化器块来打印“ hello world”[插入表情包]。
明显使用此块的主要原因是初始化静态变量。

现在,由于内部类/非静态嵌套类不允许静态变量,那么允许静态初始化程序的意义是什么呢?


-1

定义上存在矛盾:

来自JLS §8.1.3:

如果且仅当内部最靠近的方法、构造函数、实例初始化器、静态初始化器、字段初始化器或显式构造函数调用语句包含该语句或表达式是一个静态方法、静态初始化器、静态变量的变量初始化器或显式构造函数调用语句(§8.8.7),则语句或表达式在静态上下文中出现。

...

当一个内部类(其声明不在静态上下文中)引用属于词法封闭类的成员的实例变量时,使用相应词法封闭实例的变量。


1
这并没有回答问题。虽然内部类与其封闭类的实例相关联,但它仍然可以具有静态初始化程序。这并没有解释为什么JLS不允许它们有静态初始化程序的原因。(另外,我说的是静态初始化程序,而不是静态字段。) - Pacerier
关键句子是“内部类与其封闭类的实例相关联”。正如您所提到的,静态初始化器/变量/方法仅在加载类时加载一次,如果它们“绑定”到单个外部实例,那么当从其他外部实例访问时存在冲突的危险。 - Nir Alfasi
InnerClass 的实例与 OuterClass 的实例相关联,但所有的 InnerClass 实例共享同一个 Class 对象。当初始化 InnerClassClass 对象时,不会有相关联的 OuterClass 实例。它像其他任何 Class 对象一样被初始化。那么,为什么我们不能有这个 Class 对象的静态初始化器呢? - Pacerier
静态字段与实例无关,顺便说一下,这个问题与静态字段只有间接的关系。你的说法最多也只是含糊不清。 - user207421
@alfasin,关于您的更新答案:1) 根据您的第一段,这意味着假设我们允许嵌套类具有静态初始化程序,我们将在初始化程序中拥有静态上下文:class inner{ static{ /* static context due to your first paragraph, no problems here */ }}. 2) 第二段谈论的是外部类的实例变量。但是在我们内部类的静态初始化程序中,不会有任何外部类的实例变量。因此,我不明白为什么您要包含第二段。 - Pacerier
显示剩余7条评论

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