제네릭
- 형식에 의존하지 않고 하나의 값이 여러가지 데이터 타입을 가질 수 있도록 하는 것
제네릭클래스
1. 정의
class 클래스명<타입>{}
2. 구현
객체 <타입> 객체이름 = new 객체<타입>();
- 클래스에서 사용할 타입을 클래스 외부에서 설정
- 변수 타입마다 각각 클래스를 정의해야한다는 단점을 보완. 코드가 간단해짐
- Class, Interface에서만 사용 가능
- <타입>은 임의로 지정 가능, 대부분
<T>
를 사용
1. 제네릭클래스
class Sample<T>{
private T t;
Sample(T t){
this.t = t;
}
public T getT(){
return T;
}
}
2. 일반클래스
class Example{
private int a;
Example(int a){
this.a = a;
}
public int getA(){
return a;
}
}
3. 구현클래스
class SampleApp{
public static void main(String[]args){
// 1. 제네릭클래스 정의
Sample<String> a = new Sample<String>("Hello");
// 2. 일반클래스 정의
Example b = new Example(1234);
// 호출
System.out.println(a.getT); // Hello 출력(String)
System.out.println(b.getA); // 1234 출력(int)
}
}
- 제네릭은 참조데이터만 사용가능
- 기본자료형에 이용하고 싶다면 Wrapper로 객체화 해야함
- 콤마(,)를 이용하여 복수 제네릭 사용 가능 ex) class Info<K, V>
범위 제한
interface Int{ 생략 }
class Sample implemetns Int{ 생략 }
class Abc{ 생략 }
class A<T>
- T자리에 제네릭이 가능한 모든 타입 대입 가능
class A<T extends Int>
- T자리에 Int와 이를 상속/구현한 하위타입만 대입 가능
- Int가능, Sample가능
- Abc불가, String불가, Integer불가...
제네릭메서드
- 메서드에 제네릭 타입을 선언한 것
- 클래스에 제네릭 타입을 선언하지 않아도 됨
- 필요한 메서드에 제네릭타입 선언
class Box<T> {
public void add(T t) { ... } // 제네릭 메소드 아님
public T get(int index) { ... } // 제네릭 메소드 아님
public <T> T[] toArray(T[] arr) { ... } // 제네릭 메소드
//메소드의 선언부에 타입파라미터 정의
//이 메소드의 T는 Box<T>와는 완전 별개
}
class Arrays {
public static <T> void sort(T[] arr) { ... } // 제네릭 메소드
// 제네릭클래스가 아닌 곳에서도 정의 가능
와일드카드
<?> : 모든 타입의 객체를 받을 수 있음
<? Extends parent> : parent를 포함한 자손의 객체만 받을 수 있음. 상한제한
<? Super chilld> : child를 포함한 조상의 객체만 받을 수 있음. 하한제한
// TV와 Audio가 Product의 자손일 때
A<Product> a = new A<TV>(); //제네릭 타입 불일치로 에러발생
A<? extends Product> b = new A<TV>(); // 가능
A<? extends Product> c = new A<Audio>(); // 가능
// Apple이 Fruit의 자손일 때
static Juice makeJuice(FruitBox<? extends Fruit> box) {
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Juice(tmp);
}
System.out.println(Juice.makeJuice(new FruitBox<Fruit>(););
System.out.println(Juice.makeJuice(new FruitBox<Apple>(););