와일드카드는 컴퓨터에서 특정 명령어로 명령을 내릴 때, 여러 파일을 한꺼번에 지정할 목적으로 사용하는 기호를 가리킨다.
예를 들어 cmd에서 a*는 a로 시작하는 모든 글자를 의미하며, 이와 비슷한 a+는 a를 제외한 모든 a로 시작하는 문자열로 치환된다.
Bounded Wildcard는 파라미터에 대한 제한을 느슨하게 하기 위해 사용된다.
ex>
? extends E
: 파라미터로 받을 E의 확장 클래스를 허용
? super E
: 파라미터로 받을 E의 부모 클래스를 허용
불공변 (invariant) : 상속 관계에 상관없이 자신의 타입만 허용하는 것 ex> 제네릭
공변 (covariant) : 자신의 타입과 자신을 상속한 하위 클래스를 허용하는 것 ex> 배열
아래와 같이 제네릭 클래스가 있다고 가정하자
💬개발자의 의도 :
Integer, Long, Double 등의 박싱 타입은 Number의 확장이니까 Stack<Number>
객체를 만들면, pushAll
에 Iterable<Integer>
를 넘겨줘도 되겠지 ?
public class Stack<E> {
public static final int DEFAULT_SIZE = 20;
private int size;
private E[] elements;
public Stack(){
elements = (E[]) new Object[DEFAULT_SIZE]; // object를 Element로 캐스팅
size = 0;
}
public E push(E item){
elements[++size] = item;
return item;
}
public void pushAll(Iterable<E> src){
for(E e:src){
push(e);
}
}
public static void main(String[] args) {
Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers = List.of(1,2);
numberStack.pushAll(integers);
}
}
제네릭은 불공변이기 때문
pushAll
의 매개변수를 Iterable<? extends E> src
로 변경해서 E를 확장한 타입을 모두 허용
ex> Integer가 Number를 확장했으므로,Iterable<Integer>
는 pushAll의 매개 변수로 들어갈 수 있음
public class Stack<E> {
public static final int DEFAULT_SIZE = 20;
private int size;
private E[] elements;
public Stack(){
elements = (E[]) new Object[DEFAULT_SIZE]; // object를 Element로 캐스팅
size = 0;
}
public E push(E item){
elements[++size] = item;
return item;
}
public void pushAll(Iterable<? extends E> src){
for(E e:src){
push(e);
}
}
public static void main(String[] args) {
Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers = List.of(1,2);
numberStack.pushAll(integers);
}
}