Sot*_*lis 92 java interface java-8 default-method
在初始化类之前,必须初始化其直接超类,但不会初始化类实现的接口.同样,在初始化接口之前,不会初始化接口的超接口.
为了我自己的好奇心,我尝试了它,正如预期的那样,界面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();
}
Run Code Online (Sandbox Code Playgroud)
这个程序打印
implemented method
Run Code Online (Sandbox Code Playgroud)
但是,如果接口声明了default
方法,则会发生初始化.考虑InterfaceType
给出的接口为
interface InterfaceType {
public static final ClassInitializer init = new ClassInitializer();
public default void method() {
System.out.println("default method");
}
}
Run Code Online (Sandbox Code Playgroud)
然后打印上面的相同程序
static initializer
implemented method
Run Code Online (Sandbox Code Playgroud)
换句话说,static
初始化接口的字段(详细初始化过程中的步骤9)并且执行static
初始化类型的初始化器.这意味着界面已初始化.
我在JLS中找不到任何表明这应该发生的事情.不要误解我的意思,我知道如果实现类没有提供该方法的实现,这应该发生,但如果有的话呢?Java语言规范是否缺少这种情况,我是否遗漏了某些内容,还是我错误地解释了它?
Stu*_*rks 81
这是一个非常有趣的问题!
似乎JLS第12.4.1节应该明确地涵盖这一点.但是,Oracle JDK和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;
}
}
Run Code Online (Sandbox Code Playgroud)
其预期产量为:
1
j=3
jj=4
3
Run Code Online (Sandbox Code Playgroud)
确实我得到了预期的输出.但是,如果将默认方法添加到界面I
,
interface I {
int i = 1, ii = Test.out("ii", 2);
default void method() { } // causes initialization!
}
Run Code Online (Sandbox Code Playgroud)
输出变为:
1
ii=2
j=3
jj=4
3
Run Code Online (Sandbox Code Playgroud)
这清楚地表明界面I
正在被初始化,而不是之前!仅存在默认方法就足以触发初始化.不必调用或覆盖甚至提及默认方法,抽象方法的存在也不会触发初始化.
我的猜测是HotSpot实现希望避免将类/接口初始化检查添加到invokevirtual
调用的关键路径中.在Java 8和默认方法之前,invokevirtual
可能永远不会在接口中执行代码,因此不会出现这种情况.有人可能认为这是类/接口准备阶段(JLS 12.3.2)的一部分,它初始化方法表之类的东西.但也许这太过分了,不小心做了完全初始化.
我在OpenJDK compiler-dev邮件列表上提出了这个问题.还有的是一个从亚历克斯巴克利的答复中,他提出冲着JVM和lambda实施团队更多的问题(在JLS的编辑器).他还指出,在这里的规范中有一个错误,它说"T是一个类,并且T调用的静态方法被调用",如果T是一个接口,也应该适用.因此,这里可能存在规范和HotSpot错误.
披露:我在OpenJDK上为Oracle工作.如果人们认为这给了我一个不公平的优势来获得这个问题的恩惠,我愿意对它有所灵活.
Not*_*bug 13
接口未初始化,因为常量字段InterfaceType.init
(由非常量值(方法调用)初始化)不会在任何地方使用.
在编译时已知接口的常量字段不在任何地方使用,并且接口不包含任何默认方法(在java-8中),因此不需要初始化或加载接口.
接口将在以下情况下初始化,
如果是默认方法,您正在实施InterfaceType
.因此,If InterfaceType
将包含任何默认方法,它将在实现类中使用INHERITED(used).初始化将进入图片.
但是,如果要访问接口的常量字段(以正常方式初始化),则不需要接口初始化.
考虑以下代码.
public class Example {
public static void main(String[] args) throws Exception {
InterfaceType foo = new InterfaceTypeImpl();
System.out.println(InterfaceType.init);
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();
}
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,Interface将被初始化并加载,因为你正在使用该字段InterfaceType.init
.
我没有给出你在问题中已经给出的默认方法示例.
JLS 12.4.1中给出了Java语言规范和示例(示例不包含默认方法.)
我找不到JLS for Default方法,可能有两种可能性
Mar*_*o13 10
所述instanceKlass.cpp从OpenJDK的文件中包含的初始化方法InstanceKlass::initialize_impl
对应于该详细的初始化过程在JLS,其类似地在所找到的初始化在JVM规格部分.
它包含一个新步骤,该步骤在JLS中未提及,而不是在代码中引用的JVM书中:
// refer to the JVM book page 47 for description of steps
...
if (this_oop->has_default_methods()) {
// Step 7.5: initialize any interfaces which have default methods
for (int i = 0; i < this_oop->local_interfaces()->length(); ++i) {
Klass* iface = this_oop->local_interfaces()->at(i);
InstanceKlass* ik = InstanceKlass::cast(iface);
if (ik->has_default_methods() && ik->should_be_initialized()) {
ik->initialize(THREAD);
....
}
}
}
Run Code Online (Sandbox Code Playgroud)
因此,此初始化已明确实现为新的步骤7.5.这表明此实现遵循了一些规范,但似乎网站上的书面规范尚未相应更新.
编辑:作为参考,提交(从2012年10月!)其中相应的步骤已包含在实现中:http://hg.openjdk.java.net/jdk8/build/hotspot/rev/4735d2c84362
编辑2:巧合的是,我发现这篇文章关于热点中的默认方法,最后包含一个有趣的旁注:
3.7杂项
因为接口现在有字节码,所以我们必须在初始化实现类时初始化它们.
归档时间: |
|
查看次数: |
6276 次 |
最近记录: |