데이터 타입의 일반화이다. 컴파일 시점에서 미리 지정하는 방법이다.
Object 타입으로 사용하는 방법과 비교해 변환이나 검사 같은 작업을 생략할 수 있다.
타입 세이프(Type Safe)한 코드를 작성할 수 있다.
파라미터는 어떤 네이밍을 써도 되지만 주로 E, K, T, V 등을 사용한다.
파라미터는 임의의 참조형 타입을 의미한다.
클래스 뒤에 <>를 붙이고 파라미터 명을 써준다.
public class MyClass<T>{
private T t;
public MyClass(T t){
this.t = t;
}
public T get(){
return this.t;
}
}
public static void main(String[] args) {
MyClass<Integer> integerClass = new MyClass<>(1);
MyClass<String> stringClass = new MyClass<>("1");
//컴파일 에러
//MyClass<Integer> integerClass = new MyClass<>("1");
Integer integerType = integerClass.get();
String stringType = stringClass.get();
//컴파일 에러
//String integerType = integerClass.get();
}
제네릭 타입을 특정 타입의 서브 타입으로 제한하는 것
타입에 extends
키워드를 사용해 타입을 제한한다.
클래스, 인터페이스 모두 extends
키워드를 사용한다.
public class MyBounded<T extends Number>{
private T t;
public MyBounded(T t){
this.t = t;
}
public T get(){
return this.t;
}
}
public static void main(String[] args) {
MyBounded<Integer> integerClass = new MyBounded<>(1);
//컴파일 에러
//MyBounded<String> stringClass = new MyBounded<>("1");
}
메소드에서 제네릭 타입의 파라미터를 받을 때 사용한다.
?
를 사용하여 임의의 파라미터 타입으로 받을 수 있다.
Upper Bounded Wildcard : 특정 클래스의 자식 클래스만 인자로 받을 수 있다.
Lower Bounded Wildcard : 특정 클래스의 부모 클래스만 인자로 받을 수 있다.
public static void main(String[] args) {
MyClass<Integer> integerClass = new MyClass<>(1);
MyClass<String> stringClass = new MyClass<>("a");
//unbounded wildcard
printMyClass(integerClass);//1출력
printMyClass(stringClass);//a출력
//upper bounded wildcard
printNumberClass(integerClass);//1출력
//컴파일 에러
//printNumberClass(stringClass);
//lower bounded wildcard
printIntegerClass(integerClass);//1출력
//컴파일 에러
//printIntegerClass(stringClass);
}
public void printMyClass(MyClass<?> mc) {
System.out.println(mc.get());
}
public void printNumberClass(MyClass<? extends Number> mc){
System.out.println(mc.get());
}
public void printIntegerClass(MyClass<? super Integer> mc) {
System.out.println(mc.get());
}
선언부에 제네릭 타입이 있는 메서드를 제네릭 메서드라고 한다.
public <T> void printInMyClass(MyClass<T> mc){
System.out.println(mc.get());
}
반환 타입으로 제네릭 타입을 지정할 수도 있다.
public <T> T getInMyClass(MyClass<T> mc){
return mc.get();
}
메서드에 사용된 제네릭 타입은 메서드 내에서 사용된다. 그래서 static 메서드에서도 사용 가능하다.
public static <T> T getInMyClass(MyClass<T> mc){
return mc.get();
}
제네릭은 컴파일 시점에만 유효하다. 컴파일 시점에 타입에 대한 정보를 정의하고 런타임 시점에는 소거하기 때문이다. 이 과정을 타입 소거(Type Erasure)라고 한다.
타입 소거는 아래 방식으로 진행된다.