sourcecode

디폴트 방식의 인터페이스는 언제 초기화됩니까?

copyscript 2022. 9. 14. 22:22
반응형

디폴트 방식의 인터페이스는 언제 초기화됩니까?

질문에 답하기 위해 Java Language Specification을 검색하던 중 다음과 같은 사실을 알게 되었습니다.

클래스를 초기화하기 전에 직접 슈퍼클래스를 초기화해야 하지만 클래스에 의해 구현된 인터페이스는 초기화되지 않습니다.마찬가지로 인터페이스의 슈퍼 인터페이스는 인터페이스가 초기화되기 전에 초기화되지 않습니다.

궁금해서 해봤는데 예상대로 인터페이스가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

단, 인터페이스가 network를 선언한 경우defaultmethod, 그러면 초기화가 발생합니다.고려 사항: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 Language Specification에서 이 조건이 누락되어 있는지, 뭔가 누락되어 있는지, 아니면 잘못 해석하고 있는지.

이것은 매우 흥미로운 문제입니다!

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

기대했던 결과를 얻을 수 있습니다.단, 인터페이스에 기본 방식이 추가된 경우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 구현에서는 클래스/인터페이스 초기화 체크인이 중요한 경로에 추가되는 것을 피하고 싶었던 것 같습니다.invokevirtualJava 8 이전 및 기본 메서드에서는invokevirtual인터페이스에서는 코드를 실행할 수 없기 때문에, 이 문제는 발생하지 않았습니다.이것은 메서드 테이블 등을 초기화하는 클래스/인터페이스 준비 단계(JLS 12.3.2)의 일부라고 생각할 수 있습니다.그러나 이것이 너무 지나쳐서 실수로 완전한 초기화가 이루어졌을 수도 있습니다.

OpenJDK 컴파일러-dev 메일링 목록에서 이 문제를 제기했습니다.Alex Buckley(JLS의 에디터)로부터 JVM 및 람다 구현 팀을 대상으로 더 많은 질문을 받았습니다.또, 여기의 사양에는, 「T는 클래스이며, T에 의해서 선언된 스태틱 메서드가 호출되고 있다」라고 하는 버그가 있는 것도, T가 인터페이스인 경우에 적용됩니다.따라서 여기에는 사양과 HotSpot 버그가 모두 있을 수 있습니다.

공개OpenJDK 상의 Oracle에서 일하고 있습니다.만약 사람들이 이것이 제가 이 질문에 대한 보상을 받는 데 부당한 이점을 준다고 생각한다면, 저는 기꺼이 그것에 대해 융통성 있게 대처할 것입니다.

인터페이스가 초기화되지 않은 것은 상수 필드가InterfaceType.init는, 비정수치(비정수치)로 초기화되고 있습니다.

컴파일 시에 인터페이스의 일정한 필드는 어디에서도 사용되지 않으며 인터페이스에 디폴트 메서드(java-8에서는)가 포함되어 있지 않으므로 인터페이스를 초기화하거나 로드할 필요가 없습니다.

인터페이스는 다음과 같은 경우에 초기화됩니다.

  • 상수 필드는 코드에서 사용됩니다.
  • 인터페이스에는 디폴트 방식(Java 8)이 포함되어 있습니다.

디폴트 메서드의 경우,InterfaceType그래서 만약에InterfaceType에는 기본 메서드가 포함되어 있으며 구현 클래스에서 상속(사용)됩니다.「초기화」가 도입됩니다.

단, 인터페이스의 일정한 필드(통상적인 방법으로 초기화)에 액세스 하고 있는 경우는,인터페이스의 초기화는 필요 없습니다.

다음 코드를 고려해 보십시오.

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

위의 경우 필드를 사용하고 있기 때문에 인터페이스가 초기화 및 로드됩니다.InterfaceType.init.

질문에서 이미 제시하신 바와 같이 디폴트 방법의 예를 제시하지 않습니다.

Java 언어 사양 및 예는 JLS 12.4.1에 나와 있습니다(예에는 기본 메서드는 포함되어 있지 않습니다).


디폴트 메서드의 JLS를 찾을 수 없습니다.두 가지 가능성이 있습니다.

  • Java 사용자는 기본 메서드의 경우를 고려하지 않았습니다. (스펙 문서 버그)
  • 디폴트 메서드를 인터페이스의 부정수 멤버라고 부릅니다(단, no where, 다시 Specification Doc 버그에 대해 언급했습니다).

OpenJDK의 instanceKlass.cpp 파일에 초기화 방법이 포함되어 있습니다.InstanceKlass::initialize_impl이는 JLS의 Detailed Initialization Procedure에 해당하며 JVM Spec의 Initialization 섹션에 유사하게 기재되어 있습니다.

이 문서에는 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);
    ....
    }
  }
}

이 초기화는 새로운 스텝7.5로서 명시적으로 실장되어 있습니다.이것은, 이 실장이 몇개의 사양에 준거하고 있는 것을 나타내고 있습니다만, Web 사이트에 기재되어 있는 사양이 갱신되어 있지 않은 것 같습니다.

편집: 참고로 각 단계가 구현에 포함된 커밋(2012년 10월부터!) : http://hg.openjdk.java.net/jdk8/build/hotspot/rev/4735d2c84362

EDIT2: 공교롭게도 핫스팟에서 기본 메서드에 대한 이 문서를 찾았습니다.이 문서에는 마지막에 흥미로운 사이드 노트가 포함되어 있습니다.

3.7 기타

인터페이스에는 바이트 코드가 포함되어 있기 때문에 구현 클래스가 초기화되었을 때 초기화해야 합니다.

인터페이스의 초기화로 인해 서브타입에 의존하는 사이드채널 부작용이 발생하지 않도록 합니다.따라서 이것이 버그인지 아닌지는 몰라도 Java가 어떤 방식으로 수정하든 인터페이스가 초기화되는 어플리케이션에는 문제가 되지 않습니다.

의 경우class, 그것이 하위 계층이 의존하는 부작용을 일으킬 수 있다는 것은 잘 받아들여지고 있다.예를들면

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

임의의 서브클래스Foo서브클래스 코드라면 어디서든 은행에서 1000달러를 볼 수 있을 겁니다.따라서 슈퍼클래스는 서브클래스보다 먼저 초기화됩니다.

슈퍼인테이트도 똑같이 해야 하지 않을까요?유감스럽게도 슈퍼 인터페이스의 순서는 그다지 중요하지 않습니다.따라서 슈퍼 인터페이스를 초기화하는 순서는 명확하게 정의되어 있지 않습니다.

따라서 인터페이스 초기화 시 이러한 부작용을 확립하지 않는 것이 좋습니다.결국.interface는, 이러한 기능(정적 필드/스태틱)에 대응하고 있지 않습니다.

따라서 이 원칙을 따르면 인터페이스가 어떤 순서로 초기화되든 상관없습니다.

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

반응형