static <T> void sort(List<T>, Comparator<? super T> c)
메서드 선언부 반환타입 앞에 지네릭을 사용하는 것을 지네릭 메서드라 한다.
지네릭 클래스에 적용된 타입 매개변수와 지네릭 메서드의 적용된 타입 매개변수는 별개다.
이전 글에서 만들었던 makeJuice()를 지네릭 메서드로 바꾸면 이렇다.
static Juice makeJuice(FruitBox<? extends Fruit> box) {
String tmp = "";
for(Fruit fruit: box.getList()) {
tmp += fruit + " ";
}
return new Juice(tmp);
}
이 메서드를
static<T extends Fruit> Juice makeJuice(FruitBox<T> box) {
String tmp = "";
for(Fruit fruit: box.getList()) {
tmp += fruit + " ";
}
return new Juice(tmp);
}
이렇게 바꿀 수 있다.
이럴 경우 호출할 때 타입 변수를 지정해야 하지만,
보통은 컴파일러가 추정할 수 있기 때문에 생략할 수 있다.
Juicer.<Apple>makeJuicer(appleBox)
//생략가능
Juicer.makerJuicer(appleBox)
지네릭 메서드는 매개변수 타입이 복잡할 때 유용하다.
public static void printAll(List<? extends Product> list1, List<? extends Product list2)
이렇게 긴 매개변수 부분을 조금이라도 줄이고 싶을 때
public static <T enxtends Product> void printAll(List<T> list1, List<T> list2)
지네릭 메서드를 쓰면 좀 더 간결하게 쓸 수 있다. 타입 변수 T로 Product를 상속하는 타입만 받겠다는 의미는 동일하지만, 아래 코드가 좀더 간결하다.
마지막으로 약간 복잡한 제네릭 메서드다.
public static <T extends Comparable<? super T>> void sort(List<T> list)
List<T>
타입변수 T를 매개변수로 받겠다고 선언했다.
<T extends Comparable<? super T>>
'T'는 Comparable을 구현한 클래스이면서, 'T 또는 그 조상 타입을 비교하는 Comparable이어야 한다.(T가 Fruit를 상속하는 Apple이라면, Apple, Fruit, Object를 대입할 수 있다.)
지네릭 <--> non-지네릭(raw type) 형변환은 항상 가능하다.
지네릭 <--> 지네릭은 형변환 불가능하다.
와일드카드를 사용한 지네릭은 형변환 가능
Box<? extends Object> wBox = new Box<String>();
java.util.Optional 소스를 보면서 지금까지 배운 것 정리.
public final class Optional<T> {
private static final Optional<?> EMPTY = new Optional<>(); //new Optional<Object>();
private final T value;
//......
public static <T> Optional<T> empty() {
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
}
1.static상수 EMPTY에 비어있는 Optional객체를 생성해서 저장했다가 empty()를 호출하면 EMPTY를 형변환해서 반환.
Optional<?> EMPTY = new Optional<>();
-> Optional<? extends Object> EMPTY = new Optional<>();
-> Optional<? extends Object> EMPTY = new Optional<Object>();
EMPTY의 타입을 Optional<Object>가 아니라 Optional<?>로 선언한 이유는 Optional<T>로 형변환 가능하기 때문이다.
Optional<T> t = (Optional<T>) EMPTY; //<?> -> <T>
이전 버전과 호환을 위해 컴파일 할 때 제네릭 타입은 제거된다.
제네릭 타입의 경계bound 제거
<T extends Fruit>라면 T는 Fruit로 치환한다. T인 경우 T는 Object로 치환한다.
class Box<T extends Fruit> {
void add(T t) {
...
}
}
이 코드는 컴파일 후에
class Box {
void add(Fruit t) {
....
}
}```
이렇게 바뀐다.
지네릭 타입을 제거하고 타입이 일치하지 않으면 형변환을 추가한다.
T get(int i) {
return list.get(i);
}
위 코드는 컴파일 후에
Object get(int i){
return (Fruit)list.get(i);
}
이렇게 바뀐다.