🔑완벽하게 이해했나..?
💡 제네릭 클래스 Generic Class
📃 제네릭은 컴파일 시 타입을 지정하여 타입의 안정성을 보장하고 코드의 재사용성을 높이는 기능입니다. 이를 통해 타입을 명시하지 않고도 유연하게 데이터 타입을 처리할 수 있습니다
💻Generic 예시
class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
public class MyTest {
public static void main(String[] args) {
// String 타입의 Box 생성
Box<String> stringBox = new Box<>();
stringBox.setItem("안녕하세요");
String str = stringBox.getItem();
System.out.println("String 값: " + str);
// Integer 타입의 Box 생성
Box<Integer> intBox = new Box<>();
intBox.setItem(100);
int number = intBox.getItem();
System.out.println("Integer 값: " + number);
}
}
// String 값: 안녕하세요
// Integer 값: 100
⬆️ 일반적으로, 위의 예시처럼 String과 Integer 타입의 Box를 생성하려면 각각 StringBox와 IntegerBox 클래스를 작성해야 했습니다. 그러나 제네릭을 활용하면 중복 코드를 제거하고, 다양한 타입에 대해 유연한 클래스를 작성할 수 있습니다
💡 제네릭 메서드 Generic Method
📃 리턴타입 또는 매게변수의 타입을 제네릭 타입으로 선언하여 다양한 타입의 입력을 처리할 수 있습니다
💻CODE
public class GenericMethodExample {
public static <T> void printArray(T[] array) {
for (T item : array) {
System.out.print(item + " ");
}
System.out.println();
}
public static void main(String[] args) {
// Integer 배열 출력
Integer[] intArray = {1, 2, 3, 4, 5};
System.out.print("Integer Array: ");
printArray(intArray);
// String 배열 출력
String[] strArray = {"Hello", "World"};
System.out.print("String Array: ");
printArray(strArray);
// Double 배열 출력
Double[] doubleArray = {1.1, 2.2, 3.3};
System.out.print("Double Array: ");
printArray(doubleArray);
}
}
⬆️ 동일한 printArray 메서드를 Integer, String, Double 타입 배열에 대해 재사용할 수 있습니다
💡제네릭 클래스에서 범위 제한 (Bounded Type)
📃 제네릭 클래스 타입을 제한하여 특정 타입 또는 타입 계층만 허용하도록 설정할 수 있습니다
💻CODE
class NumberBox<T extends Number> {
private T number;
public void setNumber(T number) {
this.number = number;
}
public T getNumber() {
return number;
}
public double getDoubleValue() {
return number.doubleValue(); // Number 클래스 메서드 사용
}
}
public class Main {
public static void main(String[] args) {
// Integer 타입 사용
NumberBox<Integer> intBox = new NumberBox<>();
intBox.setNumber(100);
System.out.println("Integer 값: " + intBox.getNumber());
System.out.println("Double 값: " + intBox.getDoubleValue());
// Double 타입 사용
NumberBox<Double> doubleBox = new NumberBox<>();
doubleBox.setNumber(55.5);
System.out.println("Double 값: " + doubleBox.getNumber());
System.out.println("Double 값 (doubleValue): " + doubleBox.getDoubleValue());
// String 타입 사용 불가 (컴파일 오류)
// NumberBox<String> stringBox = new NumberBox<>(); // ❌ 컴파일 에러
}
}
⬆️ 이전에 작성했던 Box는 String 타입을 허용했지만, NumberBox 클래스는 Number 오브젝트를 상속 받음으로 범위를 제한할 수 있습니다
💡제네릭 메서드에서 범위 제한 (Bounded Type)
📃 제네릭 메서드에서의 범위 제한도 마찬가지로 extends를 통해 리턴 혹은 입력 타입을 제한할 수 있습니다
💻CODE
class GenericMethod {
// String 타입으로 범위 제한
public <T extends String> T method1(T t) {
return t;
}
}
public class MyTest {
public static void main(String[] args) {
GenericMethod gm = new GenericMethod();
System.out.println(gm.<String>method1("문자열")); // 가능
System.out.println(gm.method1(120)); // 불가능
}
}
⬆️ GenericMethod클래스에 method1은 String값으로 범위를 제한하고 있습니다
💡제네릭(Generic)의 활용: 와일드카드와 범위 제한
📃 Java의 제네릭에서는 와일드카드 (?), 상한(? extends T), 하한(? super T)을 활용하여 유연성과 타입 안정성을 조화롭게 유지할 수 있습니다
💻CODE
class Goods<T> {
private T t;
public T get() { return this.t; }
public void set(T t) { this.t = t; }
}
class GenericMethod {
void method1(Goods<A> g) {} // 정확히 Goods<A> 타입만 허용
void method2(Goods<?> g) {} // 모든 타입 허용
void method3(Goods<? extends B> g) {} // B 또는 B의 하위 타입만 허용
void method4(Goods<? super B> g) {} // B 또는 B의 상위 타입만 허용
}
// 클래스 계층 구조
class A {}
class B extends A {}
class C extends B {}
class D extends C {}
public class MyTest {
public static void main(String[] args) {
GenericMethod gm = new GenericMethod();
// Case 1: 정확히 Goods<A>만 허용
gm.method1(new Goods<A>()); // 가능
// gm.method1(new Goods<B>()); // 컴파일 오류
// gm.method1(new Goods<C>()); // 컴파일 오류
// gm.method1(new Goods<D>()); // 컴파일 오류
// Case 2: 모든 타입 허용 (와일드카드)
gm.method2(new Goods<A>()); // 가능
gm.method2(new Goods<B>()); // 가능
gm.method2(new Goods<C>()); // 가능
gm.method2(new Goods<D>()); // 가능
// Case 3: B와 B의 하위 타입만 허용
// gm.method3(new Goods<A>()); // 컴파일 오류
gm.method3(new Goods<B>()); // 가능
gm.method3(new Goods<C>()); // 가능
gm.method3(new Goods<D>()); // 가능
// Case 4: B와 B의 상위 타입만 허용
gm.method4(new Goods<A>()); // 가능
gm.method4(new Goods<B>()); // 가능
// gm.method4(new Goods<C>()); // 컴파일 오류
// gm.method4(new Goods<D>()); // 컴파일 오류
}
}