자바 제네릭(Generics)은 컴파일 시간에 타입의 안정성을 보장하고, 코드 재사용성을 높이기 위해 도입된 기능입니다. 제네릭을 통해 클래스, 인터페이스, 메소드를 다양한 타입으로 사용할 수 있으며, 이를 통해 중복 코드를 줄이고 더 타입 안전한 코드를 작성할 수 있습니다. 아래에서는 제네릭의 개념, 문법, 사용법, 그리고 주요 이점과 제약 사항에 대해 자세히 설명하겠습니다.
1. 제네릭의 개념
제네릭은 데이터 타입을 일반화(generalize)하여, 클래스나 메소드가 다양한 타입을 처리할 수 있도록 합니다. 제네릭을 사용하면 타입을 매개변수로 전달할 수 있습니다.
2. 제네릭의 기본 문법
제네릭은 일반적으로 <T>와 같은 형식으로 사용되며, 여기서 T는 타입 매개변수입니다. 다음은 제네릭 클래스를 정의하고 사용하는 예입니다.
제네릭 클래스
public class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
제네릭 클래스 사용
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
String str = stringBox.getValue();
Box<Integer> intBox = new Box<>();
intBox.setValue(123);
Integer num = intBox.getValue();
Box<String>은 String 타입을 다루는 박스를, Box<Integer>는 Integer 타입을 다루는 박스를 의미합니다.
3. 제네릭 메소드
메소드 또한 제네릭 타입을 가질 수 있습니다. 다음은 제네릭 메소드의 예입니다.
public class Util {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
}
위 메소드는 어떤 타입의 배열이든 받아서 그 배열의 모든 요소를 출력할 수 있습니다.
제네릭 메소드 사용
String[] stringArray = {"Hello", "World"};
Integer[] intArray = {1, 2, 3, 4};
Util.printArray(stringArray);
Util.printArray(intArray);
4. 제네릭 인터페이스
인터페이스도 제네릭 타입을 가질 수 있습니다.
public interface Comparable<T> {
int compareTo(T o);
}
5. 제네릭 타입 제한 (Bounded Type Parameters)
제네릭 타입에 제한을 걸 수 있습니다. 이는 특정 타입의 하위 클래스나 인터페이스를 타입 매개변수로 사용할 때 유용합니다.
public class NumberBox<T extends Number> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public double doubleValue() {
return value.doubleValue();
}
}
위 예제에서는 T가 Number 클래스의 하위 클래스여야만 합니다.
6. 와일드카드 (Wildcards)
제네릭에서 와일드카드는 ?로 표현되며, 불특정 타입을 나타냅니다. 와일드카드는 extends와 super 키워드와 함께 사용할 수 있습니다. 이 부분은 다음 포스팅에 자세하게 다루도록 하겠습니다.
public void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
public void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
- List<?>: 어떤 타입의 리스트라도 받을 수 있습니다.
- List<? extends Number>: Number 타입의 하위 클래스 타입을 받을 수 있습니다.
- List<? super Integer>: Integer 타입의 상위 클래스 타입을 받을 수 있습니다.
7. 제네릭의 주요 이점
- 컴파일 타임 타입 체크: 제네릭을 사용하면 컴파일 시간에 타입을 체크할 수 있어 런타임 오류를 줄일 수 있습니다.
- 코드 재사용성: 다양한 타입에 대해 동일한 코드를 재사용할 수 있습니다.
- 타입 안전성: 타입 캐스팅을 줄여 타입 안전성을 높입니다.
8. 제네릭의 제약 사항
- 기본 타입 사용 불가: 제네릭은 기본 타입(primitive type)을 사용할 수 없습니다. 예를 들어 List<int>는 사용할 수 없습니다. 대신 List<Integer>를 사용해야 합니다.
- 런타임 타입 정보 손실: 제네릭은 타입 소거(type erasure) 때문에 런타임에는 타입 정보를 알 수 없습니다. 이는 제네릭 배열 생성 등을 할 수 없는 이유입니다.
- 정적 멤버에서 사용 불가: 정적 필드나 메소드에서는 제네릭 타입 매개변수를 사용할 수 없습니다.
결론
제네릭은 자바에서 매우 강력하고 유용한 기능입니다. 제네릭을 사용하면 타입 안전성과 코드 재사용성을 크게 향상시킬 수 있으며, 컴파일 시간에 타입 오류를 방지할 수 있습니다. 제네릭의 개념과 사용법을 잘 이해하고 이를 실제 코드에 적용하면, 더욱 견고하고 유지보수하기 쉬운 프로그램을 작성할 수 있습니다. 제네릭의 제약 사항을 염두에 두면서, 적절한 상황에 제네릭을 도입하여 코드를 작성해 보시길 바랍니다.
'JAVA' 카테고리의 다른 글
Null에 대해 알아보자 (0) | 2024.08.06 |
---|---|
Java에서의 열거형(Enum)에 대해 알아보자 (0) | 2024.07.02 |
퍼사드 패턴 (0) | 2024.06.28 |
static, final, static final (0) | 2024.06.28 |
조건문에서 if만 사용할까? else if를 사용할까? (0) | 2024.06.27 |