[java] javac가 왜 불가능한 캐스트를 허용합니까?

나는 캐스팅하려고하면 StringA를 java.util.Date, 자바 컴파일러는 오류를 잡는다. 그렇다면 왜 컴파일러가 다음을 오류로 표시하지 않습니까?

List<String> strList = new ArrayList<>();
Date d = (Date) strList;

물론 JVM ClassCastException은 런타임 에을 throw 하지만 컴파일러는 플래그를 지정하지 않습니다.

동작은 javac 1.8.0_212 및 11.0.2와 동일합니다.



답변

캐스트 기술적으로 가능합니다. javac에서는 귀하의 경우와 다르지 않다는 것을 쉽게 입증 할 수 없으며 JLS는 실제로 이것을 유효한 Java 프로그램으로 정의하므로 오류 플래그를 잘못 지정합니다.

List인터페이스 이기 때문 입니다. 따라서 Date실제로 여기에 List위장한 것을 구현 하는 하위 클래스를 가질 수 있습니다 . 예를 들면 다음과 같습니다.ListDate

public class SneakyListDate extends Date implements List<Foo> {
    ...
}

그리고:

List<Foo> list = new SneakyListDate();
Date date = (Date) list; // This one is valid, compiles and runs just fine

인스턴스가 예를 들어 메소드에서 온 경우 런타임 정보가 필요하므로 이러한 시나리오를 감지하는 것이 항상 가능한 것은 아닙니다. 그럼에도 불구하고 컴파일러에는 훨씬 더 많은 노력이 필요합니다. 컴파일러는 클래스 트리를 전혀 일치시킬 수 없기 때문에 절대적으로 불가능한 캐스트 만 방지합니다. 보시다시피 여기서는 그렇지 않습니다.

JLS에서는 코드가 유효한 Java 프로그램이어야합니다. 에서 5.1.6.1. 좁혀진 참조 변환 허용 :

, 축소 참조 변환은 참조 형식에서 존재하는 S참조 형식에 T경우 모두 다음 중이 사실 :

  • […]
  • 다음 경우 중 하나적용됩니다 .
    • […]
    • S인터페이스 유형이고 T클래스 유형이며 클래스 T이름을 지정하지 않습니다 final.

따라서 컴파일러 사례가 실제로 불가능하다는 것을 알 JLS에서이를 유효한 Java 프로그램으로 정의하므로 오류를 표시 할 수 없습니다.

경고 만 표시 할 수 있습니다.


답변

예제의 일반화를 고려해 보겠습니다.

List<String> strList = someMethod();
Date d = (Date) strList;

이것이 Date d = (Date) strList;컴파일 오류가 아닌 주된 이유 입니다.

  • 직관적 인 이유는 컴파일러가 메소드 호출에 의해 반환 된 객체의 정확한 유형을 알고 있지 (일반적으로) 않는다는 것입니다. 구현 하는 클래스 일뿐 List아니라 의 하위 클래스 일 수도 있습니다Date .

  • 기술적 인 이유는 Java 언어 사양 “허용”하는 것입니다 축소 참조 변환 이이 유형 캐스트에 해당합니다. 에 따르면 JLS 5.1.6.1 :

    ” 다음 모두가 참이면 참조 유형에서 참조 유형 S으로 축소 참조 변환이 존재 T합니다.”

    5) S는 인터페이스 유형이며 T클래스 유형이며 클래스 T이름을 지정하지 않습니다 final.”

    다른 곳에서 JLS는 런타임에 예외가 발생할 수 있다고 말합니다 …

    JLS 5.1.6.1 결정은 실제 런타임 유형이 아니라 관련 변수의 선언 된 유형 에만 기반 합니다. 일반적인 경우 컴파일러는 실제 런타임 유형을 알 수 없으며 알 수 없습니다.


그렇다면 왜 Java 컴파일러가 캐스트가 작동하지 않는다고 해결할 수 없습니까?

  • 이 예에서 someMethod호출은 다양한 유형의 객체를 반환 할 수 있습니다. 컴파일러가 메소드 본문을 분석하고 리턴 될 수있는 정확한 유형 세트를 판별 할 수 있다고하더라도이를 호출하는 코드를 컴파일 한 후 다른 유형을 리턴하도록 누군가가 변경을 중지하는 것은 없습니다. 이것이 JLS 5.1.6.1이 말하는 내용의 기본 이유입니다.

  • 당신의 예에서, 스마트 컴파일러는 캐스트가 성공하지 못할 수 있음을 알아낼. 그리고 문제를 지적하기 위해 컴파일 타임 경고 를 낼 수 있습니다.

그렇다면 왜 똑똑한 컴파일러가 이것이 오류라고 말할 수는 없습니까?

  • JLS는 이것이 유효한 프로그램이라고 말합니다. 기간. 이것을 에러 라고하는 컴파일러는 Java 호환이되지 않습니다.

  • 또한 JLS와 다른 컴파일러가 유효하다고 말한 Java 프로그램을 거부하는 컴파일러 는 Java 소스 코드의 이식성을 방해합니다.


답변

5.5.1. 참조 유형 캐스팅 :

컴파일시 참조 형을 감안할 때 S(소스) 및 컴파일 시간 기준 유형 T(대상)을 주조 변환에서 존재 S
T더 컴파일 시간 오류는 다음과 같은 규칙으로 인해 발생하지 않는 경우.

[…]

경우 S인터페이스 유형은 다음과 같습니다

  • […]

  • 경우 T최종없는 클래스 또는 인터페이스 유형이 다음 슈퍼가 존재하는 경우 XT, 그리고 슈퍼 타입 Y
    S두 않도록, X그리고 Y라도 유용 별개의 매개 변수 종류를, 그리고의 소거 것을 X하고는 Y동일하며, 컴파일 타임 오류 발생합니다.

    (경우에도 때문에 그렇지 않으면, 캐스트는 항상 컴파일시에 법적 T구현하지 않는 S의 서브 클래스, T힘).

List<String>이다 S하고 Date있다 T귀하의 경우.


답변