제네릭은 클래스, 메소드 등에서 타입을 매개변수화 하는 기능이다.
데이터 타입은 외부에 의해 정해지며, 제너릭 메소드를 인스턴스화 하거나 제너릭 메소드를 호출할 때 타입을 정해줄 수 있다.
코드 유연성 : 사용 시점에서 여러 타입을 지정하여 사용할 수 있으므로 코드가 간결해지고 재사용성이 증가함
타입 안정성 : 컴파일 시점에 자동으로 검사되므로 타입 에러를 미리 방지할 수 있음
클래스 선언부에 <T>를 사용하여 제네릭 클래스를 선언할 수 있다.
// 제네릭 클래스 선언 <T>
public class LunchBox<T> {
private T food;
public T getFood() {
return food;
}
}
제네릭 클래스 LunchBox<T> 의 T 타입 파라미터에 사용자가 원하는 타입(String, Integer etc.)으로 지정할 수 있다.
// 제네릭 클래스 선언 <T>
public class Box<T> {
private T item;
public T getItem() {
return item;
}
}
public class Main {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
System.out.println(stringBox.getItem()); // String 타입 반환
Box<Integer> intBox = new Box<>();
intBox.setItem(100);
System.out.println(intBox.getItem()); // Integer 타입 반환
}
}
‼️ 제네릭 클래스의 T 에는 참조형(Reference Type)만 사용 가능. 기본형(int, double etc.)은 사용할 수 없으므로 대신 래퍼 클래스(Wrapper Class)를 사용해야함.
| 기본형 | 래퍼 클래스 |
|---|---|
int | Integer |
double | Double |
boolean | Boolean |
메소드 선언부에 <T>를 사용하여 제네릭 메소드를 사용할 수 있다.
제네릭 메소드는 클래스 제네릭 타입과 별개의 독립적인 타입 매개변수를 가진다.
// ✳️제네릭 클래스 선언
public class Box<T> {
private T item;
public T getItem() {
return item;
}
// ✳️클래스 제네릭 타입 T를 따라가는 일반 메소드
public T showItem(T item) {
System.out.println(item);
}
// ✅별개의 타입 매개변수 S를 사용하는 제네릭 메소드
public <S> getItem(S item) {
System.out.println(item);
}
}
제네릭은 컴파일 타임에만 타입 정보를 사용하고 런타임에는 타입 정보를 제거한다. 이 과정을 Type Erasure 라고 하기도 한다.
타입 매개변수 T는 런타임에는 실제 타입으로 치환되거나 (ex. 제네릭 클래스의 인스턴스를 생성하는 경우) Object로 변환된다.
// 컴파일 시점
public class Box<T> {
...
}
// 런타임 시점
public class Box<Obejct> {
...
}
기본형의 경우 Type Erasure 과정에서 Object로 변환될 수 없기 때문에 래퍼 클래스를 사용해야 한다.
제네릭 <T>에 extends 키워드를 통해 특정 클래스와 하위 타입만 받도록 타입 파라미터의 범위를 제한할 수 있다.
// 타입 매개변수 T를 Number 클래스와 하위 타입에 한정
public class GenericNumber <T extends Number> {
...
}
제네릭 클래스 GenericNumber의 타입 매개변수 T는 Number를 extends키워드를 사용하여 Number와 하위 타입인 Integer, Double 등의 타입에만 한정해서 받도록 할 수 있다.
‼️extends 키워드 사용 시 꺽쇠< > 밖에 사용하지 않도록 주의
// ✅타입 매개변수 T를 Number 클래스와 하위 타입에 한정
public class GenericNumber <T extends Number> {
...
}
// ❌GenericNumber 클래스에서 Number 클래스를 상속받음
public class GenericNumber <T> extends Number {
...
}