자바5부터 제네릭 타입이 새로 추가되었다.
제네릭을 이용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있게 되었다.
제네릭을 정확하게 알고있고 클래스를 설계한다면 클래스는 더욱 이쁘고 , 깔끔해 질 수있다고 확신한다!!
대표적인 장점 2가지
- 컴파일 시 강한 타입 체크를 할 수 있다.
- 타입 변환(casting)을 제거한다.
제네릭
제한된 타입 파라미터 <T extends 최상위 타입>
class Fruit {
public String toString(){
return "Fruit";
}
}
class Apple extends Fruit{
public String toString(){
return "Apple";
}
}
class Banana extends Fruit{
public String toString(){
return "Banana";
}
}
class Toy {
public String toString (){
return "Toy";
}
}
class Box<T> {
ArrayList<T> list = new ArrayList<T>();
void add(T item) {
list.add(item);
}
T get(int i) {
return list.get(i);
}
//크기 측정 메소드
int size() {
return list.size();
}
public String toString() {
return list.toString();
}
}
class FruitBox<T extends Fruit> extends Box<T>{ }
FruitBox<Fruit> fruitAppleBox = new FruitBox<>();
//에러
//FruitBox<Toy> fruitAppleBox = new FruitBox<>();
interface를 구현한 클래스를 제한할때는 &로 연결해서 사용한다
interface Eatable { }
class Fruit implements Eatable{ public String toString(){return "Fruit";}}
class Apple extends Fruit{ public String toString(){return "Apple";}}
class Banana extends Fruit{ public String toString(){return "Banana";}}
class Toy { public String toString (){ return "Toy";}}
class Box<T> {
ArrayList<T> list = new ArrayList<T>();
void add(T item) { list.add(item); }
T get(int i) { return list.get(i); }
int size() { return list.size(); }
public String toString() { return list.toString(); }
}
class FruitBox<T extends Fruit & Eatable> extends Box<T>{ } //Fruit 상속을 받았으며 Eatable 구현한 클래스
class Juicer {
static Juice makeJui ce (FruitBox<Fruit> box) { // <Fruit>으로 지정
String tmp = "";
for(Fruit f : box.getList()){
tmp += f + " ";
}
return new Juice(tmp);
}
}
juicer클래스는 제네릭 클래스가 아닌데다가, 제네릭 클래스라고 해도 static 메서드에는 타입 매개변수 T를 매개변수에 사용할 수 없다.
그래서 방법은 2가지다.
- 아예 제네릭을 적용하지 않던가
- 위와 같이 타입 매개변수 대신, 특정 타입을 지정해주거나
제네릭 타입이 달흔 것만으로는 오버로딩이 성립하지 않기 때문에 2번은 문제가 있다.
그러면 우리는 제네릭을 못쓰는가?
이럴 때 사용하기 위해 고안된 것이 바로 '와일드 카드' 이다.
와일드 카드
가끔 오픈소스를 보다보면 아래와 같은 문법을 볼수있습니다.
<? extends T>
<? super T>
<?>
필요성을 한번 살펴보자
<? extends T> - 와일드 카드의 상한 제한 이며 T와 그 자손들만 가능하다
<? super T> - 와일드 카드의 하한 제한 이며 , T와 그 조상들만 가능하다.
<?> - 제한이 없으면 모든 타입이 가능하며. Object와 같다!
그러면 위에 코드를 다시 고쳐보자
class Juicer {
static Juice makeJui ce (FruitBox<? extends Fruit> box) { // <Fruit>으로 지정
String tmp = "";
for(Fruit f : box.getList()){
tmp += f + " ";
}
return new Juice(tmp);
}
}
이제 이 메서드의 매개변수로 FruitBox<Fruit> 뿐만 아니라 , Fruit<Apple> , Fruit<Banana>도 가능하게 되었다.
+ 만약에 아래와 같이 Object를 상속받는다면 어떨까?
class Juicer {
static Juice makeJui ce (FruitBox<? extends Object> box) { // <Fruit>으로 지정
String tmp = "";
for(Fruit f : box.getList()){
tmp += f + " ";
}
return new Juice(tmp);
}
}
전과 달리 box의 요소가 Fruit의 자손이라는 보장이 없으므로 아래의 for문에서는 box에 저장된 요소를 Fruit 타입의 참조변수로 못받는다.
그런데!! 실제로 테스트를 해보면 문제가 없다.
이유는 ?
이미 제네릭클래스인 FruitBox를 제한했기 때문이다.
컴파이라러는 위 문장으로 부터 모든 FruitBox의 요소들이 Fruit의 자손이라는 것을 알고 있으므로 문제 삼지 않는다!
'Java' 카테고리의 다른 글
리펙토링 - 일급컬렉션 사용기 (0) | 2021.05.28 |
---|---|
상속 괜찮은가? (컴포지션) (0) | 2020.08.10 |
객체직렬화 ? (0) | 2020.02.23 |
Network - TCP 통신 (0) | 2020.01.16 |
디자인패턴(템플릿 메소드 패턴 / 팩토리 메소드 패턴) (0) | 2019.12.16 |