'super'키워드로 제네릭 경계 지정
super
유형 매개 변수가 아닌 와일드 카드에만 사용할 수있는 이유는 무엇 입니까?
예를 들어 Collection
인터페이스에서 toArray
메소드가 이렇게 작성되지 않은 이유는 무엇입니까?
interface Collection<T>{
<S super T> S[] toArray(S[] a);
}
super
<S super T>
와일드 카드 (예 :) 와 반대로 명명 된 유형 매개 변수 (예 ) 를 바인딩하는 <? super T>
것은 허용되지 않더라도 원하는대로 수행하지 않기 때문에 불법 입니다. 모든 참조 유형 Object
의 궁극 이기 때문에 super
그리고 모든 것이 Object
, 사실상 바운드가 없습니다 .
특정 예에서 참조 유형의 모든 배열은 Object[]
(자바 배열 공분산에 의해 <S super T> S[] toArray(S[] a)
) 컴파일 타임에 인수로 사용할 수 있으며 (해당 바인딩이 합법적 인 경우) ArrayStoreException
실행시 방지되지 않습니다. 시각.
당신이 제안하려는 것은 주어진 것입니다.
List<Integer> integerList;
이 가상의 super
경계가 주어지면 toArray
:
<S super T> S[] toArray(S[] a) // hypothetical! currently illegal in Java
컴파일러는 다음 만 컴파일하도록 허용해야합니다.
integerList.toArray(new Integer[0]) // works fine!
integerList.toArray(new Number[0]) // works fine!
integerList.toArray(new Object[0]) // works fine!
그리고 다른 배열 유형 인수는 없습니다 ( Integer
으로이 3 가지 유형 만 있기 때문에 super
). 즉, 이것이 컴파일되는 것을 방지하려고합니다.
integerList.toArray(new String[0]) // trying to prevent this from compiling
왜냐하면 귀하의 주장에 의해 String
는의가 아니기 때문 super
입니다 Integer
. 그러나 , Object
is a super
of Integer
, a String[]
is an Object[]
, 따라서 컴파일러 는 가상적으로 할 수 있더라도 여전히 위의 컴파일을 허용합니다 <S super T>
!
따라서 다음 은 여전히 컴파일 되며 (지금과 같이) ArrayStoreException
런타임에 제네릭 유형 경계를 사용하는 컴파일 타임 검사로 방지 할 수 없습니다.
integerList.toArray(new String[0]) // compiles fine!
// throws ArrayStoreException at run-time
제네릭과 배열은 섞이지 않으며, 이것이 보여주는 많은 장소 중 하나입니다.
배열이 아닌 예
다시, 다음과 같은 일반적인 메서드 선언이 있다고 가정 해 보겠습니다.
<T super Integer> void add(T number) // hypothetical! currently illegal in Java
그리고 다음과 같은 변수 선언이 있습니다.
Integer anInteger
Number aNumber
Object anObject
String aString
귀하의 의도는 <T super Integer>
(합법적 인 경우) add(anInteger)
, 및 add(aNumber)
, 물론 허용해야 add(anObject)
하지만 add(aString)
. 음, String
입니다 Object
때문에, add(aString)
여전히 어쨌든 컴파일 것입니다.
또한보십시오
관련 질문
제네릭 입력 규칙 :
- 내가 할 수없는 이유를 설명하는 간단한 방법이
List<Animal> animals = new ArrayList<Dog>()
있습니까? - 자바 제네릭 (아님) 공분산
- 원시 유형은 무엇이며 왜 사용하지 않아야합니까?
- 원시 유형
List
이 a와 다른 것과 어떻게 다른지 설명합니다List<Object>
.List<?>
- 원시 유형
사용 super
및 extends
:
Java Generics: What is PECS?
- 에서 효과적인 자바 2 판 : "생산자
extends
소비자super
"
- 에서 효과적인 자바 2 판 : "생산자
- 의 차이 무엇입니까
super
및extends
자바 제네릭은 <E extends Number>
과 의 차이점은 무엇입니까<Number>
?List<? extends Number>
데이터 구조에 어떻게 추가 할 수 있습니까? (당신은 할 수 없습니다!)
누구도 만족할만한 답을 제공하지 않았기 때문에 정답은 "합당한 이유가 없다"는 것 같습니다.
polygenelubricants는 그 자체로 끔찍한 기능인 자바 배열 공분산에서 발생하는 나쁜 일들에 대한 좋은 개요를 제공했습니다. 다음 코드 조각을 고려하십시오.
String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 0;
이 명백히 잘못된 코드는 "수퍼"구조에 의존하지 않고 컴파일되므로 배열 공분산을 인수로 사용해서는 안됩니다.
이제 여기 super
에 명명 된 형식 매개 변수를 요구하는 완벽하게 유효한 코드 예제가 있습니다 .
class Nullable<A> {
private A value;
// Does not compile!!
public <B super A> B withDefault(B defaultValue) {
return value == null ? defaultValue : value;
}
}
좋은 사용법을 잠재적으로 지원합니다.
Nullable<Integer> intOrNull = ...;
Integer i = intOrNull.withDefault(8);
Number n = intOrNull.withDefault(3.5);
Object o = intOrNull.withDefault("What's so bad about a String here?");
후자의 코드 조각은 B
모두 제거하면 컴파일되지 않으므로 B
실제로 필요합니다.
구현하려는 기능은 형식 매개 변수 선언의 순서를 반전하여 super
제약 조건을 extends
. 그러나 이것은 메서드를 정적 메서드로 다시 작성하는 경우에만 가능합니다.
// This one actually works and I use it.
public static <B, A extends B> B withDefault(Nullable<A> nullable, B defaultValue) { ... }
요점은이 Java 언어 제한이 실제로 다른 가능한 유용한 기능을 제한하고 있으며 추악한 해결 방법이 필요할 수 있다는 것입니다. withDefault
가상이 필요하다면 어떻게 될지 궁금합니다 .
이제 polygenelubricants가 말한 것과 상관 관계를 맺기 위해 B
여기에서 전달 된 객체 유형을 제한하지 않고 defaultValue
(예제에서 사용 된 문자열 참조) 반환하는 객체에 대한 호출자 기대치를 제한합니다. 간단한 규칙 extends
으로 원하는 유형과 super
제공하는 유형을 사용합니다.
질문에 대한 "공식"답변은 Sun / Oracle 버그 보고서 에서 찾을 수 있습니다 .
BT2 : 평가
보다
http://lampwww.epfl.ch/~odersky/ftp/local-ti.ps
특히 섹션 3과 9 페이지의 마지막 단락이 있습니다. 하위 유형 제약 조건의 양쪽에 유형 변수를 허용하면 단일 최상의 솔루션이없는 유형 방정식 집합이 생성 될 수 있습니다. 따라서 기존 표준 알고리즘을 사용하여 유형 추론을 수행 할 수 없습니다. 이것이 타입 변수가 "확장"경계만을 갖는 이유입니다.
반면 와일드 카드는 유추 할 필요가 없으므로이 제약 조건이 필요하지 않습니다.
@ ###. ### 2004-05-25
예; 핵심은 와일드 카드가 캡처 된 경우에도 추론 프로세스의 입력으로 만 사용된다는 것입니다. 결과적으로 하한이있는 것은 아무것도 추론 할 필요가 없습니다.
@ ###. ### 2004-05-26
나는 문제를 본다. 그러나 추론하는 동안 와일드 카드에 대한 하한이있는 문제와 어떻게 다른지 알 수 없습니다. 예 :
목록 <? 수퍼 넘버> s;
부울 b;
...
s = b? s : s;현재, 우리는 List <X>를 추론합니다. 여기서 X는 Object를 조건식의 유형으로 확장합니다. 즉, 할당이 불법임을 의미합니다.
@ ###. ### 2004-05-26
슬프게도 대화는 거기서 끝납니다. (현재 죽은) 링크가 가리키는 데 사용 된 논문은 Inferred Type Instantiation for GJ 입니다. 마지막 페이지를 살펴보면 다음과 같이 요약됩니다. 하한이 인정되면 유형 추론은 여러 솔루션을 생성 할 수 있으며 그중 어느 것도 원칙 이 아닙니다 .
다음이 있다고 가정합니다.
기본 클래스 A> B> C 및 D
class A{ void methodA(){} }; class B extends A{ void methodB(){} } class C extends B{ void methodC(){} } class D { void methodD(){} }
작업 래퍼 클래스
interface Job<T> { void exec(T t); } class JobOnA implements Job<A>{ @Override public void exec(A a) { a.methodA(); } } class JobOnB implements Job<B>{ @Override public void exec(B b) { b.methodB(); } } class JobOnC implements Job<C>{ @Override public void exec(C c) { c.methodC(); } } class JobOnD implements Job<D>{ @Override public void exec(D d) { d.methodD(); } }
객체에 대한 작업을 실행하는 4 가지 접근 방식을 가진 하나의 관리자 클래스
class Manager<T>{ final T t; Manager(T t){ this.t=t; } public void execute1(Job<T> job){ job.exec(t); } public <U> void execute2(Job<U> job){ U u= (U) t; //not safe job.exec(u); } public <U extends T> void execute3(Job<U> job){ U u= (U) t; //not safe job.exec(u); } //desired feature, not compiled for now public <U super T> void execute4(Job<U> job){ U u= (U) t; //safe job.exec(u); } }
사용법과 함께
void usage(){ B b = new B(); Manager<B> managerB = new Manager<>(b); //TOO STRICT managerB.execute1(new JobOnA()); managerB.execute1(new JobOnB()); //compiled managerB.execute1(new JobOnC()); managerB.execute1(new JobOnD()); //TOO MUCH FREEDOM managerB.execute2(new JobOnA()); //compiled managerB.execute2(new JobOnB()); //compiled managerB.execute2(new JobOnC()); //compiled !! managerB.execute2(new JobOnD()); //compiled !! //NOT ADEQUATE RESTRICTIONS managerB.execute3(new JobOnA()); managerB.execute3(new JobOnB()); //compiled managerB.execute3(new JobOnC()); //compiled !! managerB.execute3(new JobOnD()); //SHOULD BE managerB.execute4(new JobOnA()); //compiled managerB.execute4(new JobOnB()); //compiled managerB.execute4(new JobOnC()); managerB.execute4(new JobOnD()); }
지금 execute4를 구현하는 방법에 대한 제안이 있습니까?
========== 편집 됨 =======
public void execute4(Job<? super T> job){
job.exec( t);
}
모두에게 감사드립니다 :)
========== 편집 됨 ==========
private <U> void execute2(Job<U> job){
U u= (U) t; //now it's safe
job.exec(u);
}
public void execute4(Job<? super T> job){
execute2(job);
}
훨씬 더 나은, execute2 내부 U 코드
슈퍼 유형 U가 명명됩니다!
흥미로운 토론 :)
참조 URL : https://stackoverflow.com/questions/2800369/bounding-generics-with-super-keyword
'IT이야기' 카테고리의 다른 글
불완전한 유형의 잘못된 사용 (0) | 2021.05.01 |
---|---|
객체(트리)를 재귀적으로 반복 (0) | 2021.05.01 |
익명 유형 동적 생성 (0) | 2021.04.30 |
"git rebase origin"vs. "git rebase origin / master" (0) | 2021.04.30 |
Observable.FromEvent 대신 Observable.FromEventPattern을 사용 (0) | 2021.04.30 |