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


85

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

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호출 에 대해 불평 합니까? 그들은 매우 비슷해 보입니다.

답변:


66

의 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.


1
@Maksym 당신은 sneakyThrow전화 를 의미해야합니다 . 의 추론에 대한 특별한 규정 throws T양식은 자바 7의 사양에 존재하지 않았다
마르코 Topolnik

1
작은 nitpick :에서가 nonSneakyThrow, T해야 Exception하지 "의 슈퍼", Exception정확히 인수의 형식이 호출 사이트에서 컴파일시에 선언 때문에.
llogiq 2015

1
@llogiq 사양을 올바르게 읽었 으면의 하한 Exception과의 상한이 Throwable있으므로 추론 된 결과 유형 인 최소 상한은 Exception.
thecoop 2015

1
@llogiq 인수의 모든 상위 유형도 허용되기 때문에 인수 유형은 하한 유형 제한 만 설정합니다.
Marko Topolnik

2
"또는 Exception그 자체"문구가 독자에게 도움이 될 수 있지만 일반적으로 사양에서는 "자체 포함"이라는 의미에서 항상 "하위 유형"및 "수퍼 유형"이라는 용어를 사용한다는 점에 유의해야합니다.
Holger

17

유형 추론이 유형 변수에 대해 단일 상한을 생성하는 경우 일반적으로 상한이 솔루션으로 선택됩니다. 예를 들어이면 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경우 솔루션으로 선택 합니다. 이 경우가 있다 상한의 특정 하위 유형을 선택하는 좋은 이유.


X:>RuntimeException마지막 예제 스 니펫에서 의 의미는 무엇입니까 ?
marsouf

1

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

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

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


그래서 sneakyThrow그것은 실제로 어떤 특정 유형으로 추론되지 않고 "캐스트"는 그러한 정의되지 않은 유형에 대한 것입니까? 나는 이것으로 실제로 무슨 일이 일어나는지 궁금합니다.
Marko Topolnik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.