class Box<T> {}
Box<String> b = new Box<String>();
Box<T>
: 제네릭 클래스 - T의 Box 또는 T Box 라고 읽는다.T
: 타입변수 , 타입매개변수 ( T 는 타입문자 )Box
: 원시타입 ( raw type)<T>
에서 T 를 타입매개변수라 한다.<T>
와 같은 형태로 내부에서 사용할 타입 매개변수를 선언할 수 있다.interface Plant { ... }
class Flower implements Plant { ... }
class Rose extends Flower implements Plant { ... }
// 특정 타입만 지정가능하도록 제한
class Basket<T extends Flower & Plant> {
private T item;
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
// 에러 : static 메서드 안에서 T 사용 불가능
static int compare(T t1, T t2){...}
}
public static void main(String[] args) {
// 인스턴스화
Basket<Flower> flowerBasket = new Basket<>();
Basket<Rose> roseBasket = new Basket<>();
}
// bounded type parameters 미사용
public static <T> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e > elem) // compiler error
++count;
return count;
}
// bounded type parameters 사용시
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e.compareTo(elem) > 0)
++count;
return count;
}
<T extends Comparable<T>>
와 같이 지정하여 비교가 가능한 타입만 받도록 제한하여 해결할 수 있다.// 1 : 여기에서 선언한 타입 매개변수 T와
class Basket<T> {
...
// 2 : 여기에서 선언한 타입 매개변수 T는 서로 다른 것입니다.
public <T> void add(T element) {
...
}
// static 메서드에도 선언 가능
static <T> int setPrice(T element) {
...
}
// 메서드 정의 시점에서 사용 불가능한 경우
public <T> void print(T item) {
System.out.println(item.length()); // 불가 : 정의하는 시점에서 item이 String 인지 알 수 없으므로
System.out.println(item.equals("Kim coding")); // 가능 : Object 의 메서드는 활용 가능
}
}
제네릭타입과 원시타입간의 형변환은 가능하다. (단, 경고가 발생한다.)
대입된 타입(파라미터화 된 타입)이 다른 제네릭 타입간의 형변환
대입된 타입이 Object 일지라도 대입된 타입이 다르면 형변환이 불가능하다.
Box<Object> objBox = null;
Box<String> strBox = null;
objBox = (Box<Object>) strBox; // 에러
strBox = (Box<String>) objBox; // 에러
Box<? extends Object> wBox = new Box<String>(); // OK
Box<String>
를 Box<Object>
으로 직접 형변환하는것은 불가능
와일드카드가 포함된 제네릭 타입으로 형변환 하면 가능 (단, 확인되지않은 타입으로 형변환 한다는 경고 발생)
마찬가지로 와일드카드가 사용된 제네릭 타입끼리도 형변환이 가능하다.
List
와 List<Object>
의 차이List
나 List<Object>
가 명시된 상황에서의 차이List<String>
, List<Boolean>
과 같은 모든 타입을 수용가능하다.List<Object>
는 Object 타입을 허용한다는 의사를 컴파일러에게 명확히 전달하는 꼴List<Object>
가 아니고는 수용이 불가능하다.List<String>
은 List
의 하위 타입이지만, List<Object>
의 하위 타입은 아니다."List<String>
와 List<Object>
타입은 전혀 다른 파라미터화된 타입을 가지며Box<Integer>
→ SteelBox<Integer>
<? extends T>
<? super T>
<?>
<? extends Object>
와 같다// 와일드 카드 사용 이전 코드
// 과일박스를 넣으면 주스를 만들어 반환하는 Juicer
class Juicer {
...
// 타입매개변수 대신 특정 타입을 지정해준 상황
static Juice makeJuice ( FruitBox<Fruit> box) {
String temp = "";
for (Fruit f : box.getList()) {
temp += f + "";
}
return new Juice(temp);
}
}
FruitBox<Fruit>
로 고정시 FruitBox<Apple>
과 같은 다른 타입을 매개변수로 대입할 수 없다.static Juice makeJuice ( FruitBox<? extends Fruit> box) {
FruitBox< 모든 종류 Fruit 및 그 이하 클래스>
를 수용 가능하게된다.제네릭
: 지금은 이 타입을 모르지만, 이 타입이 정해지면 그 타입 특성에 맞게 사용하겠다.
와일드 카드
: 지금도 이 타입을 모르고, 앞으로도 모를 것이다. + 데이터타입보다는 데이터로 무엇을 할지에 관심
제네릭 타입의 경계(bound)를 제거한다.
<T extends Fruit>
과 같은 경우) 라면 T는 Fruit 으로 치환된다.<T>
의 경우 Object 로 치환된다.제네릭 타입을 제거한 후 타입이 일치하지 않으면 형변환을 추가한다.
T get(int i) {
return list.get(i); // list 의 get() 은 Object 형을 반환
}
----------------------------------
Fruit get(int i) {
return (Fruit) list.get(i);
}