Programing

기본 메소드가있는 인터페이스는 언제 초기화됩니까?

crosscheck 2020. 9. 2. 14:08
반응형

기본 메소드가있는 인터페이스는 언제 초기화됩니까?


대답 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 이이를 확실히 다루어야 할 것 같습니다 . 그러나 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;
    }
}

예상되는 출력은 다음과 같습니다.

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가 이전에 없었던 곳에서 초기화되고 있음을 명확하게 나타냅니다 ! 기본 메서드 만 있으면 초기화를 트리거 할 수 있습니다. 기본 메서드를 호출하거나 재정의하거나 언급 할 필요가 없으며 추상 메서드가 초기화를 트리거하지 않아도됩니다.

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.

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: 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.


The interface is not initialized because the constant field InterfaceType.init , which is being initialized by non constant value (method call), is not used anywhere.

It is known at compile time that constant field of interface is not used anywhere, and the interface is not containing any default method (In java-8) so there is no need to initialize or load the interface.

Interface will be initialized in following cases,

  • constant field is used in your code.
  • Interface contains a default method (Java 8)

In case of Default Methods, You are implementing InterfaceType. So, If InterfaceType will contain any default methods, It will be INHERITED (used) in implementing class. And Initialization will be into the picture.

But, If you are accessing constant field of interface (which is initialized in normal way), The interface initialization is not required.

Consider following code.

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();
}

In above case, Interface will be initialized and loaded because you are using the field InterfaceType.init.

I am not giving the default method example as you already given that in your question.

Java language specification and example is given in JLS 12.4.1 (Example does not contain default methods.)


I can not find JLS for Default methods, there may be two possibilities

  • Java people forgot to consider the case of default method. (Specification Doc bug.)
  • They just refer the default methods as non-constant member of interface. (But mentioned no where, again Specification Doc bug.)

The instanceKlass.cpp file from the OpenJDK contains the initialization method InstanceKlass::initialize_impl that corresponds to the Detailed Initialization Procedure in the JLS, which is analogously found in the Initialization section in the JVM Spec.

It contains a new step that is not mentioned in the JLS and not in the JVM book that is referred to in the code:

// 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);
    ....
    }
  }
}

So this initialization has been implemented explicitly as a new Step 7.5. This indicates that this implementation followed some specification, but it seems that the written specification on the website has not been updated accordingly.

EDIT: As a reference, the commit (from October 2012!) where the respective step has been included in the implementation: http://hg.openjdk.java.net/jdk8/build/hotspot/rev/4735d2c84362

EDIT2: Coincidentally, I found this Document about default methods in hotspot which contains an interesting side note at the end:

3.7 Miscellaneous

Because interfaces now have bytecode in them, we must initialize them at the time that an implementing class is initialized.


I'll try to make a case that an interface initialization should not cause any side-channel side effects that the subtypes depend on, therefore, whether this is a bug or not, or whichever way the Java fixes it, it should not matter to the application in which order interfaces are initialized.

In the case of a class, it is well accepted that it can cause side effects that subclasses depend on. For example

class Foo{
    static{
        Bank.deposit($1000);
...

Any subclass of Foo would expect that they'll see $1000 in the bank, anywhere in the subclass code. Therefore the superclass is initialized prior to the subclass.

Shouldn't we do the same thing for superintefaces as well? Unfortunately, the order of superinterfaces are not supposed to be significant, therefore there is no well defined order in which to initialize them.

So we better not establish this kind of side effects in interface initializations. After all, interface is not meant for these features (static fields/methods) we pile on for convenience.

Therefore if we follow that principle, it'll be no concern to us in which order interfaces are initialized.

참고URL : https://stackoverflow.com/questions/23096084/when-is-an-interface-with-a-default-method-initialized

반응형