何时是初始化默认方法的接口? [英] When is an interface with a default method initialized?

查看:123
本文介绍了何时是初始化默认方法的接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在搜索Java语言规范时,回答这个问题 ,我学会了那个

While searching through the Java Language Specification to answer this question, I learned that


在初始化类之前,它的直接超类必须是
初始化,但是类实现的接口不是
初始化。
类似地,接口的超接口在初始化接口之前不是
初始化。

Before a class is initialized, its direct superclass must be initialized, but interfaces implemented by the class are not initialized. Similarly, the superinterfaces of an interface are not initialized before the interface is initialized.

为了我自己的好奇心,我尝试了它,正如预期的那样,界面 InterfaceType 未初始化。

For my own curiosity, I tried it and, as expected, the interface InterfaceType was not initialized.

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

但是,如果接口声明了默认方法,然后初始化确实发生。考虑 InterfaceType 接口,作为

However, if the interface declares a default method, then initialization does occur. Consider the InterfaceType interface given as

interface InterfaceType {
    public static final ClassInitializer init = new ClassInitializer();

    public default void method() {
        System.out.println("default method");
    }
}

然后打印上面相同的程序

then the same program above would print

static initializer  
implemented method

换句话说,接口的 static 字段已初始化(详细初始化程序中的第9步)和 static 执行初始化类型的初始化程序。这意味着界面已初始化。

In other words, the static field of the interface is initialized (step 9 in the Detailed Initialization Procedure) and the static initializer of the type being initialized is executed. This means that the interface was initialized.

我在JLS中找不到任何迹象表明这应该发生。不要误解我的意思,我知道如果实现类没有提供该方法的实现,这应该发生,但是如果它做了什么呢? Java语言规范是否缺少这种情况,我是否遗漏了某些内容,或者我是否错误地解释了它?

I could not find anything in the JLS to indicate that this should happen. Don't get me wrong, I understand that this should happen in case the implementing class doesn't provide an implementation for the method, but what if it does? Is this condition missing from the Java Language Specification, did I miss something, or am I interpreting it wrongly?

推荐答案

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

This is a very interesting issue!

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

It seems like JLS section 12.4.1 ought to cover this definitively. However, the behavior of Oracle JDK and OpenJDK (javac and HotSpot) differs from what's specified here. In particular, the Example 12.4.1-3 from this section covers interface initialization. The example as follows:

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

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

and indeed I get the expected output. However, if a default method is added to 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 正在被初始化,而不是之前!仅存在默认方法就足以触发初始化。默认方法不必被调用或覆盖甚至提及,抽象方法的存在也不会触发初始化。

which clearly indicates that interface I is being initialized where it wasn't before! The mere presence of the default method is enough to trigger the initialization. The default method doesn't have to be called or overridden or even mentioned, nor does the presence of an abstract method trigger initialization.

我的猜测是HotSpot实现通缉避免将类/接口初始化检查添加到 invokevirtual 调用的关键路径中。在Java 8和默认方法之前, invokevirtual 永远不会在接口中执行代码,因此不会出现这种情况。有人可能会认为这是类/接口准备阶段的一部分( JLS 12.3.2 )初始化方法表之类的东西。但也许这太过分了,而且意外地完成了初始化。

My speculation is that the HotSpot implementation wanted to avoid adding class/interface initialization checking into the critical path of the invokevirtual call. Prior to Java 8 and default methods, invokevirtual could never end up executing code in an interface, so this didn't arise. One might think this is part of the class/interface preparation stage (JLS 12.3.2) which initializes things like method tables. But perhaps this went too far and accidentally did full initialization instead.

在OpenJDK compiler-dev邮件列表上提出了这个问题。有一个 Alex Buckley的回复(编辑)他提出了更多针对JVM和lambda实施团队的问题。他还指出,这里的规范中存在一个错误,它说T是一个类,并且T调用的静态方法被调用,如果T是一个接口,也应该适用。所以,这里可能存在规范和HotSpot错误。

I've raised this question on the OpenJDK compiler-dev mailing list. There's been a reply from Alex Buckley (editor of the JLS) in which he raises more questions directed at the JVM and lambda implementation teams. He also notes that there's a bug in the spec here where it says "T is a class and a static method declared by T is invoked" should also apply if T is an interface. So, it might be that there are both specification and HotSpot bugs here.

Disclosure :我在OpenJDK上为Oracle工作。如果人们认为这给我一个不公平的优势来获得这个问题的恩惠,我愿意灵活应对它。

Disclosure: I work for Oracle on OpenJDK. If people think this gives me an unfair advantage at getting the bounty attached to this question, I'm willing to be flexible about it.

这篇关于何时是初始化默认方法的接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆