Generic은 입력되는 객체의 타입을 보장하기 위해 사용된다. 입력으로 정해진 객체 혹은 그의 부모, 자식 등으로 다양한 객체 타입을 하나의 코드로 사용할 수 있는 편리한 기능이다.
// java.util.List
public interface List<E> {
boolean add(E e);
// 이하 코드 생략
}
// 초기화시 타입에 대한 선언이 가능하다.
List<Foo> foos = new ArrayList();
foos.add(new Foo("bar"));
Generic으로 구현된 메소드의 경우에는 선언된 타입으로만 매개변수를 입력해야한다. 이를 상속받은 클래스, 혹은 부모 클래스를 매개변수로 사용하고 싶어도 불가능하며, 혹은 그 어떤 타입이 와도 상관 없는 경우에 대응하기 좋지 않다. 이를 위한 해법으로 Wildcard를 사용한다
WildCard에는 3가지 종류가 있다
Unbound Wildcard는 List<?>와 같은 형태로 물음표만 가지고 정의 되어진다. 내부적으로는 Object로 정의 되어서 사용되고 모든 타입을 인자로 받을 수 있다. 타입 파라미터에 의존하지 않는 메소드만을 사용하거나 Object 메소드에서 제공하는 기능으로 충분한 경우에 사용한다.
Upper bounded wildcard는 List<? extends Foo>와 같은 형태로 사용되고, 특정 클래스의 자식 클래스만을 인자로 받겠다는 선언이다. 예시에서는 Foo 클래스를 상속받은 어떤 클래스가 와도 되지만 사용할 수 있는 기능은 Foo클래스에 정의된 기능만 사용가능 하다. 주로 변수의 제한을 완화하게 하기 위해서 사용된다
Lower bounded wildcard는 List<? super Foo>와 같은 형태로 사용되고, Upper bounded와는 반대로 특정 클래스의 부모클래스만을 인자로 받겠다는 선언이다. 예시에서는 Foo클래스의 부모인 어떤 객체도 인자로 올 수 있지만, 사용할때는 Object로 취급 된다.
// Foo.java
public class Foo {
public void foo() {
// do something;
}
}
// Bar.java
public class Bar extends Foo {
public void bar() {
// do something
}
}
이런식으로 상속관계의 Foo, Bar가 있다고 했을때
// WildCard.java
public class WildCard {
public <T extends Foo> void test(List<T> fooList){
fooList.get(0).foo();
fooList.get(0).bar(); // Error
// Foo를 상속받은 Bar의 메서드이지만 Bar의 존재를 알 수 없기때문에 이는 불가능하다
}
}
// Unbound
List<?> unboundList = new ArrayList<>();
// Upper bound
List<? extends Foo> upperList = new ArrayList<>();
// Lower bound
List<? super Bar> lowerList = new ArrayList<>();
WildCard wildCard = new WildCard();
wildCard.test(upperList);
wildCard.test(lowerList); // Error
// Bar의 super class중에 Foo가 있다고 해도 파라미터로 넘길 수 없다
참조1 https://developer-syubrofo.tistory.com/42
참조2 https://offbyone.tistory.com/327