何时使用默认方法初始化接口?

当搜寻通过Java语言规范来回答这个问题),我学到的是

在初始化类之前,必须先初始化其直接超类, 同样,在初始化接口之前,不会初始化接口的超级接口。

出于我自己的好奇心,我尝试了一下,并且未如预期的那样对接口InterfaceType进行了初始化。

public class Example {

public static void main(String[] args) throws Exception {

InterfaceType foo = new InterfaceTypeImpl();

foo.method();

}

}

class InterfaceTypeImpl implements InterfaceType {

@Override

public void method() {

System.out.println("implemented method");

}

}

class ClassInitializer {

static {

System.out.println("static initializer");

}

}

interface InterfaceType {

public static final ClassInitializer init = new ClassInitializer();

public void method();

}

该程序打印

implemented method

但是,如果接口声明了一个default方法,则确实会发生初始化。考虑InterfaceType给定的接口

interface InterfaceType {

public static final ClassInitializer init = new ClassInitializer();

public default void method() {

System.out.println("default method");

}

}

然后上面的相同程序将打印

static initializer  

implemented method

换句话说,static接口的字段已初始化(详细初始化过程中的步骤9),并static执行了正在初始化的类型的初始化程序。这意味着接口已初始化。

我在JLS中找不到任何东西来表明这应该发生。不要误会我的意思,我知道如果实现类没有提供该方法的实现,应该发生这种情况,但是如果实现了该怎么办?Java语言规范中是否缺少这种情况?我错过了什么吗?还是我误解了?

回答:

这是一个非常有趣的问题!

似乎JLS第12.4.1节应该明确地涵盖这一点。但是,OracleJDK和OpenJDK(javac和HotSpot)的行为与此处指定的行为不同。特别是,本节的示例12.4.1-3涵盖了接口初始化。示例如下:

interface I {

int i = 1, ii = Test.out("ii", 2);

}

interface J extends I {

int j = Test.out("j", 3), jj = Test.out("jj", 4);

}

interface K extends J {

int k = Test.out("k", 5);

}

class Test {

public static void main(String[] args) {

System.out.println(J.i);

System.out.println(K.j);

}

static int out(String s, int i) {

System.out.println(s + "=" + i);

return i;

}

}

其预期输出为:

1

j=3

jj=4

3

实际上我得到了预期的输出。但是,如果将默认方法添加到interface I

interface I {

int i = 1, ii = Test.out("ii", 2);

default void method() { } // causes initialization!

}

输出更改为:

1

ii=2

j=3

jj=4

3

这清楚地表明接口I正在被初始化!仅使用默认方法就足以触发初始化。默认方法不必被调用,重写或提及,抽象方法的存在也不会触发初始化。

我的猜测是,HotSpot实现希望避免在invokevirtual调用的关键路径中添加类/接口初始化检查。在Java

8和默认方法之前,invokevirtual永远不会最终在接口中执行代码,因此这没有发生。可能有人认为这是类/接口准备阶段(JLS12.3.2)的一部分,该阶段初始化诸如方法表之类的东西。但这可能太过分了,而意外地进行了完全初始化。

我在OpenJDK编译器-

dev邮件列表中提出了这个问题。AlexBuckley(JLS的编辑)收到了答复,他提出了更多针对JVM和lambda实现团队的问题。他还指出,规范中存在一个错误,该错误表明“

T是一个类,并且调用了T声明的静态方法”,如果T是接口,则也应适用。因此,这里可能同时存在规范和HotSpot错误。

:我在OpenJDK上为Oracle工作。如果人们认为这使我在悬赏这个问题上获得了不公平的优势,那么我愿意对此灵活。

以上是 何时使用默认方法初始化接口? 的全部内容, 来源链接: utcz.com/qa/399073.html

回到顶部