Java 제네릭 마스터하기: 재사용 가능하고 타입 안전한 코드 작성
Java 제네릭: 재사용 가능하고 타입 안전한 코드 작성
이 가이드는 Java 제네릭, 제네릭 클래스와 메서드 작성법, 이점들을 명확한 예제와 함께 설명합니다.
Java 제네릭은 컴파일 타임 안전성을 유지하면서 다양한 데이터 타입에서 작동하는 단일 클래스, 인터페이스, 메서드를 작성할 수 있게 합니다.
참고: 제네릭은 프리미티브 타입(int, float, char 등)과 작동하지 않습니다.
Java 제네릭 클래스
타입 매개변수를 선언하면 모든 객체 타입에서 작동하는 클래스를 생성할 수 있습니다.
예제: 제네릭 클래스 생성
class Main {
public static void main(String[] args) {
GenericsClass<Integer> intObj = new GenericsClass<>(5);
System.out.println("Generic Class returns: " + intObj.getData());
GenericsClass<String> stringObj = new GenericsClass<>("Java Programming");
System.out.println("Generic Class returns: " + stringObj.getData());
}
}
class GenericsClass<T> {
private T data;
public GenericsClass(T data) {
this.data = data;
}
public T getData() {
return this.data;
}
}
실행 결과
Generic Class returns: 5
Generic Class returns: Java Programming
위 예제에서 GenericsClass는 T가 타입 매개변수인 제네릭 클래스입니다. 두 인스턴스가 생성됩니다:
- intObj - T가
Integer로 대체되어 클래스가 정수와 작동합니다.
- stringObj - T가
String으로 대체되어 클래스가 문자열과 작동합니다.
Java 제네릭 메서드
메서드도 제네릭일 수 있어 타입 안전성을 유지하면서 모든 타입의 인수를 받을 수 있습니다.
예제: 제네릭 메서드 생성
class Main {
public static void main(String[] args) {
DemoClass demo = new DemoClass();
demo.<String>genericsMethod("Java Programming");
demo.<Integer>genericsMethod(25);
}
}
class DemoClass {
public <T> void genericsMethod(T data) {
System.out.println("Generics Method:");
System.out.println("Data Passed: " + data);
}
}
실행 결과
Generics Method:
Data Passed: Java Programming
Generics Method:
Data Passed: 25
컴파일러가 타입을 추론할 수 있으면 명시적 타입 인수를 생략할 수 있습니다:
demo.genericsMethod("Java Programming");
제한된 타입 (Bounded Types)
때로는 제네릭 타입이 특정 클래스 패밀리만 허용하길 원합니다. 제한된 타입은 extends 키워드로 이를 달성합니다.
문법: <T extends A> - T는 A 또는 A의 하위 타입이 될 수 있습니다.
예제: 제한된 타입
class GenericsClass <T extends Number> {
public void display() {
System.out.println("This is a bounded type generics class.");
}
}
class Main {
public static void main(String[] args) {
// GenericsClass<String> obj = new GenericsClass<>(); // 컴파일 타임 에러
}
}
여기서 GenericsClass는 숫자 타입(Integer, Double 등)만 허용합니다. String으로 인스턴스화하려 하면 컴파일 타임 에러가 발생합니다:
GenericsClass<String> obj = new GenericsClass<>();
^ reason: inference variable T has incompatible bounds
equality constraints: String
lower bounds: Number
where T is a type-variable:
T extends Number declared in class GenericsClass
Java 제네릭의 장점
1. 코드 재사용성
제네릭 코드는 여러 데이터 타입에서 작동하여 중복을 줄입니다. 예를 들어 단일 메서드가 정수, 문자열, 커스텀 객체를 처리할 수 있습니다.
2. 컴파일 타임 타입 검사
타입 매개변수가 컴파일 타임에 알려지므로 컴파일러는 프로그램이 실행되기 전에 타입 불일치를 감지할 수 있습니다.
3. 컬렉션과의 원활한 통합
Java의 컬렉션 프레임워크는 제네릭 기반으로, 컬렉션이 타입 안전성을 강제할 수 있게 합니다. 예:
ArrayList<String> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
다른 컬렉션 클래스(LinkedList, Queue, Map 등)도 동일한 이점을 위해 제네릭을 사용합니다.