이것은 타사 라이브러리 API의 실제 예이지만 단순화되었습니다.
Oracle JDK 8u72로 컴파일
다음 두 가지 방법을 고려하십시오.
<X extends CharSequence> X getCharSequence() {
return (X) "hello";
}
<X extends String> X getString() {
return (X) "hello";
}
둘 다 “체크되지 않은 캐스트”경고를보고합니다. 이유가 있습니다. 나를 방해하는 것은 왜 전화 할 수 있는지
Integer x = getCharSequence();
그리고 컴파일? 컴파일러는 Integer
구현하지 않는 것을 알고 있어야합니다 CharSequence
. 전화
Integer y = getString();
예상대로 오류를 제공합니다
incompatible types: inference variable X has incompatible upper bounds java.lang.Integer,java.lang.String
누군가 왜이 행동이 유효한 것으로 설명 할 수 있습니까? 어떻게 유용할까요?
클라이언트는이 호출이 안전하지 않다는 것을 모릅니다. 클라이언트 코드는 경고없이 컴파일됩니다. 컴파일에서 오류에 대해 경고하지 않는 이유는 무엇입니까?
또한이 예제와 어떻게 다른가요?
<X extends CharSequence> void doCharSequence(List<X> l) {
}
List<CharSequence> chsL = new ArrayList<>();
doCharSequence(chsL); // compiles
List<Integer> intL = new ArrayList<>();
doCharSequence(intL); // error
전달하려고하면 List<Integer>
예상대로 오류가 발생합니다.
method doCharSequence in class generic.GenericTest cannot be applied to given types; required: java.util.List<X> found: java.util.List<java.lang.Integer> reason: inference variable X has incompatible bounds equality constraints: java.lang.Integer upper bounds: java.lang.CharSequence
이것이 오류로보고되면 왜 Integer x = getCharSequence();
그렇지 않습니까?
답변
CharSequence
입니다 interface
. 따라서 SomeClass
구현하지 않아도 CharSequence
클래스를 완벽하게 만들 수 있습니다.
class SubClass extends SomeClass implements CharSequence
그러므로 당신은 쓸 수 있습니다
SomeClass c = getCharSequence();
유추 된 유형 X
이 교차 유형 이기 때문 SomeClass & CharSequence
입니다.
이것은 최종 Integer
이기 때문에 조금 이상 Integer
하지만 final
이 규칙에서 아무런 역할을하지 않습니다. 예를 들어 쓸 수 있습니다
<T extends Integer & CharSequence>
반면에, String
되지 interface
는 연장 불가능하다, 그래서 SomeClass
의 하위 유형을 얻기 위해 String
자바 클래스를위한 다중 상속을 지원하지 않기 때문에.
이 List
예에서는 제네릭이 공변량 또는 공변량이 아님을 기억해야합니다. 즉 , X
의 하위 유형 인 경우 의 하위 유형도 수퍼 유형도 아닙니다 . 때문에 구현하지 않습니다 , 당신은 사용할 수 없습니다 에Y
List<X>
List<Y>
Integer
CharSequence
List<Integer>
doCharSequence
메소드 .
그러나 이것을 컴파일 할 수 있습니다.
<T extends Integer & CharSequence> void foo(List<T> list) {
doCharSequence(list);
}
당신은 방법이있는 경우 반환List<T>
같은를 :
static <T extends CharSequence> List<T> foo()
넌 할 수있어
List<? extends Integer> list = foo();
또한 유추 된 유형이이고이 유형 Integer & CharSequence
이의 하위 유형이기 때문 입니다 Integer
.
교차 경계 유형은 여러 경계를 지정하면 암시 적으로 발생합니다 (예 🙂 <T extends SomeClass & CharSequence>
.
자세한 내용은 여기 타입이 일을 경계 어떻게 설명 JLS의 일부입니다. 여러 인터페이스를 포함 할 수 있습니다 (예 :
<T extends String & CharSequence & List & Comparator>
그러나 첫 번째 경계 만 비 인터페이스 일 수 있습니다.
답변
에 할당하기 전에 컴파일러에서 유추하는 유형 X
은 Integer & CharSequence
입니다. 이 유형 은 최종적 이기 때문에 이상하게 느껴지Integer
지만 Java에서는 완벽하게 유효한 유형입니다. 그런 다음로 캐스팅되어 Integer
완벽하게 괜찮습니다.
Integer & CharSequence
유형에 대해 정확히 하나의 가능한 값이 null
있습니다.. 다음과 같은 구현으로 :
<X extends CharSequence> X getCharSequence() {
return null;
}
다음 과제가 작동합니다.
Integer x = getCharSequence();
이 가능한 값으로 인해 할당이 명백히 쓸모없는 경우에도 할당이 잘못된 이유는 없습니다. 경고가 유용 할 것입니다.
실제 문제는 호출 사이트가 아닌 API입니다.
사실, 나는 최근 에이 API 디자인 안티 패턴에 대해 블로그를 작성했습니다 . 유추 된 유형이 전달 될 것이라고 (거의) 절대 보장 할 수 없기 때문에 임의 유형을 리턴하는 일반 메소드를 설계하지 않아야합니다. 예외는 Collections.emptyList()
목록의 공허함과 일반적인 유형 삭제가 추론 <T>
이 작동하는 이유 인 경우와 같습니다.
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}