Home > Backend > Java > ☕️[Java] 제네릭(Generic)

☕️[Java] 제네릭(Generic)
Java Programming Language Backend

1️⃣ 제네릭(Generic)

Java에서의 제네릭(Generic) 은 클래스나 메서드에서 사용할 데이터 타입을 나중에 지정할 수 있도록 하는 기능입니다.

제네릭을 사용하면 코드의 재사용성을 높이고, 컴파일 시 타입 안전성을 제공하며, 명시적 타입 캐스팅을 줄일 수 있습니다.

2️⃣ 제네릭(Generic)의 주요 개념.

  1. 타입 매개변수 :
    • 제네릭 클래스나 메서드는 타입 매개변수를 사용하여 타입을 정의합니다. 이 타입 매개변수는 클래스나 메서드가 호출될 때 구체적인 타입으로 대체됩니다.
  2. 타입 안정성 :
    • 제네릭을 사용하면 컴파일 시 타입을 검사하므로, 런타입에 발생할 수 있는 타입 오류를 줄일 수 있습니다.
  3. 재사용성 :
    • 제네릭 클래스나 메서드는 다양한 타입에 대해 동작하도록 설계할 수 있어, 코드의 재사용성을 높입니다.

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️⃣ 제네릭의 제한 사항.

  1. Primitive Type 사용 불가 : 제네릭은 참조 타입만 허용하며, 기본 타입은 사용할 수 없습니다.
// 올바르지 않음
Box<int> intBox = new Box<>(); // 컴파일 오류
  1. 정적 컨텍스트에서의 타입 매개변수 사용 : 정적 메서드나 정적 변수에서는 타입 매개변수를 사용할 수 없습니다.
public class GenericClass<T> {
    private static T item; // 컴파일 오류
}
  1. 제네릭 배열 생성 불가 : 제네릭 배열을 직접 생성할 수 없습니다.
// 올바르지 않음
T[] array = new T[10]; // 컴파일 오류

제네릭은 Java의 강력한 기능으로, 타입 안전성을 높이고 코드의 재사용성을 극대화할 수 있게 해줍니다.
이를 적절히 활용하면 더 안정적이고 유지보수하기 쉬운 코드를 작성할 수 있습니다.