제네릭(Generic)이란?
"결정되지 않은 타입을 파라미터로 처리하고 실제 사용할 때 파라미터를 구체적인 타입으로 대체시키는 기능
public class Box <T> {
pulic T content;
}
- Box클래스에서 결정되지 않은 content의 타입을 T라는 타입 파라미터로 정의한 것.
- <T>는 T가 타입 파라미터임을 뜻하는 기호로, 타입이 필요한 자리에 T를 사용할 수 있음을 알려주는 역할
Box<String> box = new Box<String>();
box.content = "문자열예시";
Box<Integer> box = new Box<>(); //생성자 호출시 생성자 타입 생략 가능
- 이때 기본 타입은 타입 파라미터의 대체 타입이 될 수 없음 -> int, double, char와 같은 기본형 타입 사용 불가능
- 반드시 Wrapper 클래스(Integer, Double, Character) 사용
- Why? - 타입 소거(Type Erasure)
- 제네릭은 컴파일 타임에만 타입을 체크하고, 런타임에는 타입 정보가 소거되기 때문
제네릭 타입
- 결정되지 않은 타입을 파라미터로 가지는 클래스와 인터페이스
public class 클래스명<A, B, >{ }
pulic interface 인터페이스명<A, B, >{ }
- 외부에서 제네릭 탕비을 사용하려면 타입 파라미터에 구체적인 타입을 지정해야 함
- 지정하지 않으면 Object타입이 암묵적으로 사용됨
- Product 클래스를 제네릭 타입으로 생성
public class Product<K, M>{
//필드
private K kind;
private M model;
//메소드
public K getKind(){ return this.kind;}
public M getModel(){ return this.model;}
public void setKind(K kind){ this.kind = kind;}
public void setModel(M model){ this.model = model;}
}
public class Tv{}
- Product 제네릭 타입을 이용해 Tv 정보를 저장하고 얻는 방법
public class GenericEX{
public static void main(String[] args){
Product<Tv, String> product1 = new Product<>();
product1.setKind(new Tv());
product1.setModel("스마트 tv");
Tv tv = product1.getKind();
String tvModel = product1.getModel();
}
}
제네릭 메소드
- 타입 파라미터를 가지고 있는 메소드
- 타입 파라미터가 메소드 선언부에 정의된다는 점에서 제네릭 타입과 차이가 있음
public <A, B> 리턴타입 메소드명(매개변수){ }
//예시
public class Box<T>{
private T t;
}
public class EX{
public static <T> Box<T> boxing(T t){
Box<T> box = new Box<T>();
return box;
}
}
제한된 타입 파라미터
- 경우에 따라 타입 파라미터를 대체하는 구체적인 타입을 제한할 필요가 있음
- 예를 들어 숫자 연산하는 제네릭 메소드는 대체 타입으로 Number 또는 자식 클래스로 제한할 필요가 있음
- 이처럼 모든 타입으로 대체할 수 없고 특정 타입과 자식 또는 구현 관계에 있는 타입에만 대체할 수 있는 타입 파라미터를 제한된 타입 파라미터라고 함
public <T extends 상위타입> 리턴타입 메소드명(매개변수){ }
//예시
public <T extends Number> boolean compare(T t1, T t2){
double v1 = t1.doubleValue();
double v2 = t2.doubleValue();
return (v1 == v2);
}
와일드카드 타입 파라미터
- 제네릭 타입을 매개값이나 리턴 타입으로 사용할 때 타입 파라미터로 ?(와일드 카드)를 사용할 수 있음
- ?는 범위에 있는 모든 타입으로 대체할 수 있다는 표시
- 일반 와일드카드
public void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
- Upper Bounded Wildcard
- ? extends Type는 타입이 특정 클래스 또는 그 하위 클래스임을 나타냄
public double sumList(List<? extends Number> list) {
double sum = 0.0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
- Lower Bounded Wildcard
- ? super Type는 타입이 특정 클래스 또는 그 상위 클래스임을 나타냄
public void addNumber(List<? super Integer> list) {
list.add(10); // Integer 또는 그 상위 타입에 값을 추가할 수 있음
}
제네릭의 한계
- 런타임 타입 소거(Type Erasure)
- 제네릭은 컴파일 타임에만 존재하며, 런타임 시에는 소거됨
- 따라서 런타임에 제네릭 타입 정보는 알 수 없음
public static void printType(List<String> list) {
if (list instanceof List<String>) { // 컴파일 오류
// 불가능
}
}
- Primitive 타입 사용 불가
- 제네릭은 레퍼런스 타입만 지원하므로 기본형 타입은 사용할 수 없음
- 대신 Wrapper 클래스(예: Integer, Double)를 사용
List<int> list = new ArrayList<>(); // 오류
- 정적 컨텍스트에서 사용 제한
- 정적 변수나 정적 메서드에서 타입 파라미터를 사용할 수 없음
public class MyClass<T> {
private static T value; // 오류
}
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] 컬렉션 자료구조 (1) | 2025.02.04 |
---|---|
[Java] 멀티 스레드 (1) | 2025.01.23 |