컴파일러가 관련없는 인터페이스 유형으로 호출 될 때 클래스 유형 매개 변수를 사용하여이 일반 메소드를 선택하는 이유는 무엇입니까?


11

다음 두 클래스와 인터페이스를 고려하십시오.

public class Class1 {}
public class Class2 {}
public interface Interface1 {}

왜 두 번째 호출 하는가 mandatory에 오버로드 된 메소드를 호출 Class2, 경우 getInterface1Interface1아무 관계가 없다 Class2?

public class Test {

    public static void main(String[] args) {
        Class1 class1 = getClass1();
        Interface1 interface1 = getInterface1();

        mandatory(getClass1());     // prints "T is not class2"
        mandatory(getInterface1()); // prints "T is class2"
        mandatory(class1);          // prints "T is not class2"
        mandatory(interface1);      // prints "T is not class2"
    }

    public static <T> void mandatory(T o) {
        System.out.println("T is not class2");
    }

    public static <T extends Class2> void mandatory(T o) {
        System.out.println("T is class2");
    }

    public static <T extends Class1> T getClass1() {
        return null;
    }

    public static <T extends Interface1> T getInterface1() {
        return null;
    }
}

Java 8 Java 7과의 호환성깨뜨렸다는 것을 이해합니다 .

$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac -source 1.7 -target 1.7 *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test
warning: [options] bootstrap class path not set in conjunction with -source 1.7
1 warning
T is not class2
T is not class2
T is not class2
T is not class2

그리고 Java 8 (11 및 13으로 테스트 됨) :

$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test                        
T is not class2
T is class2
T is not class2
T is not class2

1
결론 : Java에서 메소드 오버로드는 많은 놀라움을 가져다 주므로 매우 신중하게 사용해야합니다. 응답의 복잡성에 의해 입증 된 것처럼 유형 매개 변수의 바인딩으로 만 두 개의 과부하를 식별하는 것은 문제를 요구합니다. 기본적으로 코드의 각 독자에게 코드를 이해하기 전에 해당 답변을 읽고 이해하도록 요청합니다. 달리 말하면 : 유형 유추가 개선 될 때 프로그램이 중단되면 안전한 영역에 있지 않은 것입니다. 행운을 빕니다!
Stephan Herrmann

답변:


4

Java 8에서는 형식 유추 규칙이 크게 개선되었으며 대상 유형 유추가 크게 개선되었습니다. 따라서 Java 8 이전에는 메소드 인수 사이트가 기본적으로 삭제 된 유형 ( Class1for getClass1()Interface1for getInterface1())으로 추론을받지 못했습니다 .Java 8에서는 가장 적합한 적용 가능한 유형이 추론됩니다. JLS for Java 8에서는 새로운 장인 18 장. JLS for Java 7에서 누락 된 유형 유추 를 소개했습니다 .


가장 구체적인 적용 가능한 유형은 <T extends Interface1>입니다 <X extends RequiredClass & BottomInterface>. 여기서 RequiredClass컨텍스트에 필요한 클래스 BottomInterface는 모든 인터페이스 (포함 Interface1) 의 최하위 유형입니다 .

참고 : 각 Java 유형은로 표시 될 수 있습니다 SomeClass & SomeInterfaces. 보낸 사람 RequiredClass의 하위 유형 SomeClassBottomInterface의 하위 유형 SomeInterfaces, X모든 자바 유형의 하위 유형입니다. 따라서 XJava 하단 유형입니다.

XJava 하단 유형 이므로 public static <T> void mandatory(T o)public static <T extends Class2> void mandatory(T o)메소드 서명 모두 와 일치합니다 X.

그래서,에 따라 §15.12.2 , mandatory(getInterface1())의 가장 구체적인 오버로드를 호출 mandatory()이다 방법, public static <T extends Class2> void mandatory(T o)이후 <T extends Class2>보다 더 구체적입니다 <T>.

메소드 시그니처 getInterface1()와 일치하는 결과를 리턴하도록 유형 매개 변수를 명시 적으로 지정하는 방법은 다음과 같습니다 public static <T extends Class2> void mandatory(T o).

public static <T extends Class2 & Interface1> void helper() {
    mandatory(Test.<T>getInterface1()); // prints "T is class2"
}

가장 구체적인 적용 가능한 유형은 <T extends Class1>입니다 <Y extends Class1 & BottomInterface>. 여기서 BottomInterface모든 인터페이스의 최하위 유형입니다.

Ypublic static <T> void mandatory(T o)메소드 서명 과 일치 하지만 확장되지 않으므로 public static <T extends Class2> void mandatory(T o)메소드 서명 과 일치 Y하지 않습니다 Class2.

그래서 mandatory(getClass1())호출 public static <T> void mandatory(T o)방법.

with와 달리 type 매개 변수를 getInterface1()명시 적으로 지정 getClass1()하여 public static <T extends Class2> void mandatory(T o)메서드 서명 과 일치하는 결과를 반환하도록 할 수는 없습니다 .

                       java: interface expected here
                                     
public static <T extends Class1 & C̲l̲a̲s̲s̲2> void helper() {
    mandatory(Test.<T>getClass1());
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.