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


52

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

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

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

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


2
List여기서 특별한 것은 없습니다 . Date d = (Date) new Object();
엘리엇 프리쉬

1
나는 최근에 arduino와 놀고 있습니다. 나는 캐스트를 행복하게 받아들이지 않고 완전히 예측할 수없는 결과로 컴파일러를 좋아했습니다. 문자열을 정수로? 확실한 것! 정수로 두배? 알겠습니다! 부울 문자열? 적어도 그 중 하나는 대부분 거짓이됩니다 ...
Stian Yttervik

@ElliottFrisch : Date와 Object 사이에는 명백한 상속 관계가 있지만 Date와 List 사이에는 관계가 없습니다. 그래서 컴파일러가 String에서 Date로 캐스트를 플래그 지정하는 것과 같은 방식 으로이 캐스트에 플래그를 지정했습니다. 그러나 Zabuza가 훌륭한 답변에서 설명했듯이 List는 인터페이스이므로 strListList를 구현하는 클래스의 인스턴스 라면 캐스트가 합법적 입니다.
Mike Woinoski

이것은 자주 반복되는 질문이며 여러 번 중복 된 것으로 보입니다. 이것은 기본적으로 강력하게 관련된 버전입니다 : stackoverflow.com/questions/21812289/…
헐크

1
@StianYttervik -fpermissive가 그 일을하고 있습니다. 컴파일러 경고를 켭니다.
bobsburner

답변:


86

캐스트 기술적으로 가능합니다. 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 프로그램으로 정의하므로 오류를 표시 할 수 없습니다.

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


16
그리고 주목할 가치가 있습니다. String을 사용하는 경우 String이 최종이기 때문에 컴파일러는 클래스를 확장 할 수 없다는 것을 알고 있습니다.
MTilsted

5
실제로, 그것이 문자열의 "최종성"이라고 생각하지 않습니다 myDate = (Date) myString. JLS 용어를 사용하여 명령문은 S( String)를 T( ) 로 변환하려고 시도합니다 Date. 여기서는 S인터페이스 유형이 아니므로 위에서 인용 한 JLS 조건이 적용되지 않습니다. 예를 들어, 달력을 날짜로 캐스트하면 클래스가 최종이 아니더라도 컴파일러 오류가 발생합니다.
Mike Woinoski

1
컴파일러가 strList가 ArrayList 유형 일 수 있음을 입증하기 위해 컴파일러가 충분한 정적 분석을 수행 할 수 없는지 여부는 알 수 없습니다.
여호수아

3
컴파일러는 검사가 금지되지 않습니다. 그러나 그것을 오류라고 부르는 것은 금지되어 있습니다. 그러면 컴파일러가 호환되지 않습니다. (내 답변보기 ...)
Stephen C

3
약간의 용어를 추가하려면 컴파일러가 타입이 있음을 입증 할 필요가 Date & List있다 살 수없는 ,이되는 것을 증명하는 것만으로는 충분하지 않습니다 무인 (그것은 미래에있을 수 있습니다) 현재.
Polygnome

15

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

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 소스 코드의 이식성을 방해합니다.


4
호출 클래스를 컴파일 한 후 호출 된 함수 구현이 변경 될 수 있다는 사실에 찬성 하십시오 . 따라서 컴파일 타임에 호출 가능한 경우에도 현재 수신자의 구현으로 캐스트가 불가능하므로 나중에 런타임에는 그렇지 않을 수 있습니다 수신자가 변경 또는 교체되었을 때.
베드로-모니카 복원 복원

2
컴파일러가 너무 똑똑해 지려고 할 때 발생할 수있는 이식성 문제를 강조합니다.
Mike Woinoski

2

5.5.1. 참조 유형 캐스팅 :

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

[...]

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

  • [...]

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

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.