1️⃣ 제네릭(Generic)
Java에서의 제네릭(Generic) 은 클래스나 메서드에서 사용할 데이터 타입을 나중에 지정할 수 있도록 하는 기능입니다.
제네릭을 사용하면 코드의 재사용성을 높이고, 컴파일 시 타입 안전성을 제공하며, 명시적 타입 캐스팅을 줄일 수 있습니다.
2️⃣ 제네릭(Generic)의 주요 개념.
-
타입 매개변수 :
- 제네릭 클래스나 메서드는 타입 매개변수를 사용하여 타입을 정의합니다. 이 타입 매개변수는 클래스나 메서드가 호출될 때 구체적인 타입으로 대체됩니다.
-
타입 안정성 :
- 제네릭을 사용하면 컴파일 시 타입을 검사하므로, 런타입에 발생할 수 있는 타입 오류를 줄일 수 있습니다.
-
재사용성 :
- 제네릭 클래스나 메서드는 다양한 타입에 대해 동작하도록 설계할 수 있어, 코드의 재사용성을 높입니다.
3️⃣ 제네릭 클래스.
제네릭 클래스는 클래스 선언에 타입 매개변수를 포함하여 정의합니다.
일반적으로 타입 매개변수는 한 글자로 표현 되며, ‘T(Tyep)
‘, ‘E(Element)
‘, ‘K(Key)
‘, ‘V(Value)
‘ 등이 자주 사용됩니다.
예제.
// Box 클래스
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
// Main 클래스
public class Main {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
System.out.println("String item: " + stringBox.getItem()); // String item: Hello
Box<Integer> integerBox = new Box<>();
integerBox.setItem(123);
System.out.println("Integer item: " + integerBox.getItem()); // Integer item: 123
}
}
4️⃣ 제네릭 메서드.
제네릭 메서드는 메서드 선언 타입 매개변수를 포함하여 정의합니다.
예제.
public class GenericMethodExample {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4, 5};
String[] strArray = {"A", "B", "C", "D"};
printArray(intArray); // 1 2 3 4 5
printArray(strArray); // A B C D
}
}
5️⃣ 제네릭 타입 제한 (Bounded Type Parameters)
제네릭 타입 매개변수에 제한을 걸어 특정 타입의 하위 클래스나 인터페이스만 허용할 수 있습니다.
상한 제한 (Upper Bound)
public class BoundedTypeExample<T extends Number> {
private T number;
public BoundedTypeExample(T number) {
this.number = number;
}
public void printNumber() {
System.out.println("Number: " + number);
}
public static void main(String[] args) {
BoundedTypeExample<Integer> intExample = new BoundedTypeExample<>(123);
intExample.printNumber(); // Number: 123
BoundedTypeExample<Double> doubleExample = new BoundedTypeExample<>(45.67);
doubleExample.printNumber(); // Number: 45.67
}
}
여기서 ‘T’ 는 ‘Number’ 클래스나 그 하위 클래스만 될 수 있습니다.
하한 제한 (Lower Bound)
하한 제한은 와일드카드(’? super T
‘)를 사용하여 정의됩니다.
예를 들어 ‘List<? super Integer>
‘ 는 ‘Integer
‘ 의 상위 타입인 ‘Number
‘, ‘Object
‘ 등이 될 수 있습니다.
import java.util.ArrayList;
import java.util.List;
public class LowerBoundWildcardExample {
public static void addNumbers(List<? super Integer> list) {
for (int i = 0; i < 5; i++) {
list.add(i);
}
}
public static void main(String[] args) {
List<Number> numberList = new ArrayList<>();
addNumbers(numberList);
System.out.println(numberList); // [0, 1, 2, 3, 4]
}
}
6️⃣ 제네릭의 제한 사항.
- Primitive Type 사용 불가 : 제네릭은 참조 타입만 허용하며, 기본 타입은 사용할 수 없습니다.
// 올바르지 않음
Box<int> intBox = new Box<>(); // 컴파일 오류
- 정적 컨텍스트에서의 타입 매개변수 사용 : 정적 메서드나 정적 변수에서는 타입 매개변수를 사용할 수 없습니다.
public class GenericClass<T> {
private static T item; // 컴파일 오류
}
- 제네릭 배열 생성 불가 : 제네릭 배열을 직접 생성할 수 없습니다.
// 올바르지 않음
T[] array = new T[10]; // 컴파일 오류
제네릭은 Java의 강력한 기능으로, 타입 안전성을 높이고 코드의 재사용성을 극대화할 수 있게 해줍니다.
이를 적절히 활용하면 더 안정적이고 유지보수하기 쉬운 코드를 작성할 수 있습니다.