이것은 타사 라이브러리 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의 하위 유형 인 경우 의 하위 유형도 수퍼 유형도 아닙니다 . 때문에 구현하지 않습니다 , 당신은 사용할 수 없습니다 에YList<X>List<Y>IntegerCharSequenceList<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;
}
