[java] Java 8의 예외 유형 추론의 독특한 기능

이 사이트에서 다른 답변에 대한 코드를 작성하는 동안 다음과 같은 특징을 발견했습니다.

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또는 ThrowableRuntimeException경우 솔루션으로 선택 합니다. 이 경우가 있다 상한의 특정 하위 유형을 선택하는 좋은 이유.


답변

sneakyThrow, 유형 T바운드 제네릭 형식의 변수입니다 없이 특정 유형이 (유형의 출처가 없기 때문에).

을 사용 nonSneakyThrow하면 유형 T이 인수와 동일한 유형이므로 예제에서 Tof nonSneakyThrow(e);Exception입니다. 으로는 testSneaky()선언하지 않는 던져Exception 오류가 표시됩니다.

이것은 확인 된 예외가있는 Generics의 알려진 간섭입니다.


답변