이 사이트에서 다른 답변에 대한 코드를 작성하는 동안 다음과 같은 특징을 발견했습니다.
static void testSneaky() {
final Exception e = new Exception();
sneakyThrow(e); //no problems here
nonSneakyThrow(e); //ERRROR: Unhandled exception: java.lang.Exception
}
@SuppressWarnings("unchecked")
static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
throw (T) t;
}
static <T extends Throwable> void nonSneakyThrow(T t) throws T {
throw t;
}
첫째, 왜 sneakyThrow
호출이 컴파일러에 대해 괜찮은지 혼란 스럽습니다 . T
확인되지 않은 예외 유형에 대한 언급이없는 경우 가능한 유형은 무엇입니까 ?
둘째, 이것이 작동한다는 것을 받아들이면 왜 컴파일러가 nonSneakyThrow
호출 에 대해 불평 합니까? 그들은 매우 비슷해 보입니다.
답변
의 T는 sneakyThrow
로 추론됩니다 RuntimeException
. 유형 추론에 대한 언어 사양 ( http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html ) 에서 따를 수 있습니다.
첫째, 섹션 18.1.3에 메모가 있습니다.
양식의 경계
throws α
는 순전히 정보 제공 용입니다. 가능한 경우 확인 된 예외 유형이 아니도록 α의 인스턴스화를 최적화하도록 해결을 지시합니다.
이것은 아무 영향도 미치지 않지만, 특별한 경우에 유추 된 예외 유형에 대한 자세한 정보를 제공하는 Resolution 섹션 (18.4)을 알려줍니다.
… 그렇지 않으면 결합 세트가 포함되어있는 경우
throws αi
, 및 αi 적절한 상한은 기껏해야하고,Exception
,Throwable
, 및Object
, 그때의 Ti =RuntimeException
.
이 경우에 적용됩니다 sneakyThrow
-유일한 상한은 Throwable
이므로 사양에 따라 T
추론 RuntimeException
되므로 컴파일됩니다. 메서드의 본문은 중요하지 않습니다. 확인되지 않은 캐스트는 실제로 발생하지 않기 때문에 런타임에 성공하고 컴파일 타임 체크 예외 시스템을 무력화 할 수있는 메서드가 남습니다.
nonSneakyThrow
그 방법의이 같은 컴파일되지 않습니다 T
낮은의 하한 가지고있다 Exception
(즉 T
의 슈퍼해야한다 Exception
, 또는 Exception
그이, 그래서 그것 때문에이 호출되고있는 유형으로, 체크 예외 인 자체) T
로 추정됩니다 Exception
.
답변
유형 추론이 유형 변수에 대해 단일 상한을 생성하는 경우 일반적으로 상한이 솔루션으로 선택됩니다. 예를 들어이면 T<<Number
해는 T=Number
입니다. Integer
, Float
등도 제약 조건을 충족 할 수 있지만 대신 선택할 이유가 없습니다 Number
.
즉 위해도의 경우와 throws T
자바 5-7 : T<<Throwable => T=Throwable
. (Sneaky throw 솔루션에는 모두 명시 적 <RuntimeException>
유형 인수가 있으며, 그렇지 않으면 <Throwable>
추론됩니다.)
java8에서는 람다가 도입되면서 문제가 발생합니다. 이 경우를 고려하십시오
interface Action<T extends Throwable>
{
void doIt() throws T;
}
<T extends Throwable> void invoke(Action<T> action) throws T
{
action.doIt(); // throws T
}
빈 람다로 호출하면 무엇 T
으로 추론됩니까?
invoke( ()->{} );
에 대한 유일한 제약 T
은 상한 Throwable
입니다. java8의 초기 단계에서는 T=Throwable
추론됩니다. 내가 제출 한이 보고서를 참조하십시오 .
그러나 이것은 Throwable
빈 블록에서 확인 된 예외 를 추론하기에는 꽤 어리석은 일 입니다. 보고서에서 해결책이 제안되었습니다 (JLS에서 채택한 것으로 보입니다)-
If E has not been inferred from previous steps, and E is in the throw clause,
and E has an upper constraint E<<X,
if X:>RuntimeException, infer E=RuntimeException
otherwise, infer E=X. (X is an Error or a checked exception)
즉, 상한이 Exception
또는 Throwable
인 RuntimeException
경우 솔루션으로 선택 합니다. 이 경우가 있다 상한의 특정 하위 유형을 선택하는 좋은 이유.
답변
로 sneakyThrow
, 유형 T
바운드 제네릭 형식의 변수입니다 없이 특정 유형이 (유형의 출처가 없기 때문에).
을 사용 nonSneakyThrow
하면 유형 T
이 인수와 동일한 유형이므로 예제에서 T
of nonSneakyThrow(e);
는 Exception
입니다. 으로는 testSneaky()
선언하지 않는 던져Exception
오류가 표시됩니다.
이것은 확인 된 예외가있는 Generics의 알려진 간섭입니다.