1.5 와일드 카드 ~ 1.8 지네릭 타입의 제거
✏️ 와일드 카드
- 제네릭 타입은 컴파일러가 컴파일할 때만 사용하고 제거해버림.
그래서 오버로딩 개념이 아니라, 메서드 중복 정의가 되기 때문에
'<?>' 개념을 사용한다.
-
<> 사이에 T,E,K,V 이것은 다 같은 동작을 하고 네이밍은 관습
ex) E는 List, ArrayList 와 같은 컬렉션을 사용할때 사용한다.T 유형을 의미합니다.
E 엘리먼트 ( List: 엘리먼트 목록)
K 열쇠입니다 (에서 Map<K,V>)
V 값 (반환 값 또는 매핑 된 값)입니다. -
하지만 '?'만으로는 Obj와 다를게 없으므로 extents와 super 사용
'<? extends T>' 와일드 카드의 상한제한. T와 그 자손들만 가능
'<? super T>' 와일드 카드의 하한 제한. T와 그 조상들만 가능
'<?>' 제한없음. 모든 타입이 가능 '<? extends Object>'와 동일 -
지넥릭 클래스와 달리 와일드 카드에서는 '&' 사용 불가능.
-
예제 12-3
import java.util.ArrayList;
// 클래스 선언부
class Fruit { public String toString() { return "Fruit";}}
class Apple extends Fruit { public String toString() { return "Apple";}}
class Grape extends Fruit { public String toString() { return "Grape";}}
// Juice 클래스
class Juice {
String name;
Juice(String name) { this.name = name = "Juice"; }
public String toString() { return name; }
}
// syso 출력할 주스를 만들어줌 (담겨있는 객체 뽑아냄, 믹서기 안의 과일들)
class Juicer {
static Juice makeJuice(FruitBox<? extends Fruit> box) {
String tmp = "";
for(Fruit f : box.getList())
// 요기가 핵심 부분. box가 Fruit이 아닐 수 있는데, 아니라면 오류가 발생할것임.
// 하지만 하단에 FruitBox의 와일드카드 지네릭클래스 FruitBox를 Fruit으로
// 제한했기 때문에 컴파일러는 위 문장으로부터 모든 FruitBox의 요소들이 Fruit
// 자손이라는 것을 알고 있으므로 문제 삼지 않음.
tmp += f + " ";
return new Juice(tmp);
}
}
// 메인코드 믹서기에 주스를 담고 주스만들기
class FruitBoxEx3 {
public static void main(String[] args){
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
fruitBox.add(new Apple());
fruitBox.add(new Grape());
appleBox.add(new Apple());
appleBox.add(new Apple());
System.out.println(Juicer.makeJuice(fruitBox));
System.out.println(Juicer.makeJuice(appleBox));
} // main
}
// FruitBox가 Fruit과 Fruit을 상속받고있는 그 조상들만 받아들일 수 있도록 약속
class FruitBox<T extends Fruit> extends Box<T> {}
// Box클래스, 믹서기 안에서의 기능
class Box<T> {
ArrayList<T> list = new ArrayList<T>();
void add(T item) { list.add(item); }
T get(int i) { return list.get(i); }
ArrayList<T> getList() { return list; }
int size() { return list.size(); }
public String toString() { return list.toString(); }
}
- 예제 12-4
import java.util.;
// Fruit Vo
class Fruit {
String name;
int weight;
Fruit(String name, int weight){
this.name = name;
this.weight = weight;
}
public String toString() { return name+"("+weight+")";}
}
// Apple 클래스
class Apple extends Fruit {
Apple(String name, int weight){
// 생성자를 통해서 Fruit 부모객체에 name, weight 주입
// 복습
// 2주전? 에 배운것처럼 이런 상황에서 super(); 를 호출 했을때 에러가 날것이다.
// 왜냐하면 Fruit 객체는 기본 생성자 ()가 없기 때문.
super(name, weight);
}
}
class Grape extends Fruit {
Grape(String name, int weight){
super(name, weight);
}
}
// Comparator 인터페이스를 상속, compare 오버라이딩
class AppleComp implements Comparator<Apple> {
public int compare(Apple t1, Apple t2) {
retunr t2.weight - t1.weight;
}
}
class AppleComp implements Comparator<Grape> {
public int compare(Grape t1, Grape t2) {
retunr t2.weight - t1.weight;
}
}
class AppleComp implements Comparator<Fruit> {
public int compare(Fruit t1, Fruit t2) {
retunr t1.weight - t2.weight;
}
}
class FruitBoxEx4 {
public static void main(String[] args) {
FruitBox<Apple> appleBox = new FruitBox<Apple>();
FruitBox<Grape> appleBox = new FruitBox<Grape>();
appleBox.add(new Apple("GreenApple", 300));
appleBox.add(new Apple("GreenApple", 100));
appleBox.add(new Apple("GreenApple", 200));
grapeBox.add(new Grape("GreenApple", 400));
grapeBox.add(new Grape("GreenApple", 300));
grapeBox.add(new Grape("GreenApple", 200));
Collections.sort(appleBox.getList(), new AppleComp());
Collections.sort(grapeBox.getList(), new GrapeComp());
System.out.println(appleBox);
System.out.println(grapeBox + "\n");
Collections.sort(appleBox.getList(), new FruitComp());
Collections.sort(grapeBox.getList(), new FruitComp());
System.out.println(appleBox);
System.out.println(grapeBox);
} // main
}
class FruitBox<T extends Fruit> extends Box<T> {}
ckass Box<T> {
ArrayList<T> list = new ArrayList<T>();
void add(T item) {
list.add(item);
}
T get(int i){
return list.get(i);
}
ArrayList<T> getList() { return list; }
int size() {
return list.size();
}
public String toString() {
return list.toString();
}
}
- 위 예제의 핵심은... 여러 클래스를 Comparator나 sort등등 처리를 할때, 항상 다른 타입으로
받아들여야 하는지임. FruitComp() 를 통해서 자손이 생길때마다 반복해서 같은 코드를 작성하는것이 아니라 <? super T>를 붙여 줌으로써 하한제한. 즉 자신을 포함한 조상객체의 접근을 허용하는 코드를 작성해서 자손이 늘어나도 조상에 접근 할 수 있도록 함이 핵심이다.
✏️ 지네릭 메서드
- 메서드 선언부에 지네릭 타입이 선언된 메서드를 지네릭 메서드라 한다.
static <T> void sort(List<T> list, Comparator<? super T> c)
// 선언부의 T와 매개변수의 T는 전혀 다른 별개의 것이다.
앞에 나왔던 makeJuice 메서드를 지네릭 메서드로 바꾸는 예시
static Juice makeJuice(FruitBox<? extends Fruit> box){
}
static <T extends Fruit> Juice makeJuice(FruitBox<T> box){
}
- 이 메서드를 호출할 때에는 대입된 타입을 표시해야 하는데, 클래스 내부에서 호출 할때에는 생략 가능하지만 이게 컴파일러가 타입을 추정할 수 없을때는 생략 불가능(말이 너무 어려워서 한동안 고민했는데, 이게 선언이 현재 되어있는지를 보는거 같습니다. 모르는 객체라면 불가능)
✏️ 지네릭 타입의 형변환
- 지네릭 메소드나 객체 자체 형변환은 안되지만 타입 내부에서의 형변환은 가능하다
그래서 매개변수의 다형성이 적용될 수 있다. - ? , T 로 표현 함으로서 가능성을 열어두고 개발할 수 있다.
✏️ 지네릭 타입의 제거
- 컴파일러는 지네릭 타입을 이용해서 소스파일을 체크하고, 필요한 곳에 형변환을 넣어준다. 그리고 형변환을 넣어주고 지네릭을 제거한다. 즉, 컴파일된 파일에는 지넬기 타입에 대한 정보가 없다.
Author And Source
이 문제에 관하여(1.5 와일드 카드 ~ 1.8 지네릭 타입의 제거), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@chamominedev/1.5-와일드-카드-1.8-지네릭-타입의-제거저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)