developer tip

이 Java 8 프로그램이 컴파일되지 않는 이유는 무엇입니까?

optionbox 2020. 10. 15. 07:44
반응형

이 Java 8 프로그램이 컴파일되지 않는 이유는 무엇입니까?


이 프로그램은 Java 7 (또는를 사용하는 Java 8 -source 7)에서 제대로 컴파일되지만 Java 8에서는 컴파일되지 않습니다.

interface Iface<T> {}
class Impl implements Iface<Impl> {}

class Acceptor<T extends Iface<T>> {
    public Acceptor(T obj) {}
}

public class Main {
    public static void main(String[] args) {
        Acceptor<?> acceptor = new Acceptor<>(new Impl());
    }
}

결과:

Main.java:10: error: incompatible types: cannot infer type arguments for Acceptor<>
        Acceptor<?> acceptor = new Acceptor<>(new Impl());
                                           ^
    reason: inference variable T has incompatible bounds
      equality constraints: Impl
      upper bounds: Iface<CAP#1>,Iface<T>
  where T is a type-variable:
    T extends Iface<T> declared in class Acceptor
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Iface<CAP#1> from capture of ?
1 error

즉, 이것은 Java 7과 8 사이 하위 소스 비 호환성입니다. Java SE 8과 Java SE 7 사이의 비 호환성 목록을 살펴 봤지만 내 문제에 맞는 것을 찾지 못했습니다.

그래서 이것은 버그입니까?

환경:

$ /usr/lib/jvm/java-8-oracle/bin/java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)

신고 해 주셔서 감사합니다. 이것은 버그처럼 보입니다. 내가 처리하고 왜 이런 일이 발생하는지에 대한 추가 정보가 있으면 더 나은 답변을 추가 할 것입니다. 나는 그것을 추적하기 위해이 버그 항목 JDK-8043926 을 제출 했습니다.


Java 언어 사양은 유형 추론 과 관련하여 크게 변경되었습니다 . JLS7에서는 유형 추론§15.12.2.7§15.12.2.8 에 설명되어있는 반면, JLS8에는 18 장 유형 추론에 대한 전체 장이 있습니다 .

규칙은 JLS7과 JLS8 모두에서 매우 복잡합니다. 차이점을 말하기는 어렵지만 섹션 §18.5.2 에서 분명히 알 수 있듯이 분명히 차이점이 있습니다 .

이 추론 전략은 The Java Language Specification [..]의 Java SE 7 Edition과 다릅니다.

그러나 변경의 의도는 이전 버전과 호환되도록하는 것이 었습니다. 섹션 §18.5.2 의 마지막 단락을 참조하십시오 .

[..]이 전략은 일반적인 사용 사례에서 합리적인 결과를 제공하며 Java 언어 사양의 Java SE 7 Edition의 알고리즘과 역 호환됩니다.

그게 사실인지 아닌지 모르겠습니다. 그러나 문제를 나타내지 않는 몇 가지 흥미로운 코드 변형이 있습니다. 예를 들어 다음 문은 오류없이 컴파일됩니다.

new Acceptor<>(new Impl());

이 경우 대상 유형 이 없습니다 . 즉, Class Instance Create Expression폴리 표현식 이 아니며 유형 추론에 대한 규칙 이 더 간단합니다. §18.5.2 참조 :

호출이 폴리 표현식이 아닌 경우 바운드 세트 B 3 을 B 2 와 동일하게하십시오 .

이것이 다음 문장이 작동하는 이유이기도합니다.

Acceptor<?> acceptor = (Acceptor<?>) new Acceptor<>(new Impl());

표현식 컨텍스트에 유형이 있지만 대상 유형으로 계산되지 않습니다 . 경우 클래스 인스턴스 생성식이 양자 택일에 발생하지 않습니다 대입 표현식 또는 호출 표현 , 다음은 될 수 없다 폴리 표현 . §15.9 참조 :

클래스 인스턴스 생성 식은 클래스에 대한 형식 인수에 다이아몬드 형식을 사용하고 할당 컨텍스트 또는 호출 컨텍스트 (§5.2, §5.3)에 표시되는 경우 폴리 식 (§15.2)입니다. 그렇지 않으면 독립형 표현식입니다.

귀하의 진술로 돌아갑니다. JLS8의 관련 부분은 다시 §18.5.2 입니다. 그러나 JLS8에 따라 다음 문장이 올바른지, 컴파일러가 오류 메시지와 함께 올바른지 말할 수 없습니다. 그러나 적어도 추가 정보에 대한 몇 가지 대안과 지침이 있습니다.

Acceptor<?> acceptor = new Acceptor<>(new Impl());

유형 추론은 Java 8에서 변경되었습니다. 이제 유형 추론은 생성자와 메소드 모두에 대해 대상 유형과 매개 변수 유형을 모두 살펴 봅니다. 다음을 고려하세요:

interface Iface {}
class Impl implements Iface {}
class Impl2 extends Impl {}

class Acceptor<T> {
    public Acceptor(T obj) {}
}

<T> T foo(T a) { return a; }

다음은 이제 Java 8에서 괜찮습니다 (Java 7에서는 아님).

Acceptor<Impl> a = new Acceptor<>(new Impl2());

// Java 8 cleverly infers Acceptor<Impl>
// While Java 7 infers Acceptor<Impl2> (causing an error)

물론 이것은 두 가지 모두에서 오류를 제공합니다.

Acceptor<Impl> a = new Acceptor<Impl2>(new Impl2());

Java 8에서도 괜찮습니다.

Acceptor<Impl> a = foo (new Acceptor<>(new Impl2())); 

// Java 8 infers Acceptor<Impl> even in this case
// While Java 7, again, infers Acceptor<Impl2>
//   and gives: incompatible types: Acceptor<Impl2> cannot be converted to Acceptor<Impl>

다음은 둘 다에 오류를 제공하지만 오류는 다릅니다.

Acceptor<Impl> a = foo (new Acceptor<Impl2>(new Impl2()));

// Java 7:
// incompatible types: Acceptor<Impl2> cannot be converted to Acceptor<Impl>

// Java 8:
// incompatible types: inferred type does not conform to upper bound(s)
//     inferred: Acceptor<Impl2>
//     upper bound(s): Acceptor<Impl>,java.lang.Object

Clearly, Java 8 made the type inference system smarter. Does this cause incompatibilities? Generally, no. Because of type erasure, it doesn't actually matter what types were inferred, as long as the program compiles. Does Java 8 compile all Java 7 programs? It should, but you brought up a case where it does not.

What seems to be happening is that Java 8 is not handling wildcards well. Instead of considering them as a lack of a constraint, it seems to be treating them as a restrictive constraint that it can't satisfy. I'm not sure if it's following the letter of the JLS, but I'd call this a bug at least in spirit.

FYI, this does work (note that my Acceptor doesn't have the type constraints that yours does):

Acceptor<?> a = new Acceptor<>(new Impl2());

Do note that your example is using a wildcard type outside of a method parameter (which is inadvisable), I wonder if the same issue will happen in more typical code that uses the diamond operator in method calls. (Probably.)

참고URL : https://stackoverflow.com/questions/23063474/why-does-this-java-8-program-not-compile

반응형