다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시 타입을 체크해주는 기능
기존의 컬렉션 프레임워크는 Object 타입을 사용하여 모든 종류의 객체를 하나의 자료구조에 저장할 수 있었다. 이로 인해 컬렉션의 요소를 활용하기 위해 다운캐스팅을 수행하는 경우가 많았는데, 이는 런타임 시에 타입 불일치 오류(ClassCastException)를 발생시킬 수 있었다. 이에 제네릭스를 도입함으로써 컴파일러가 타입을 체크하여 잘못된 타입 사용을 사전에 방지할 수 있게 되었고(런타임 오류 사전 방지), 코드의 가독성도 높아졌다.
Java에서 데이터 타입을 일반화하는 방법으로, 클래스나 메소드 사용할 데이터 타입을 특정(Specific)타입으로 미리 정하는 게 아니라, 필요에 의해 지정할 수 있도록 일반(Generic)타입으로 지정하는 것이다.
즉 제네릭은 하나의 코드를 다양한 타입의 객체에 재사용할 수 있는 객체지향기법이다.
데이터타입을 내부에서 지정하는 것이 아닌 외부에서 지정함으로써 사용하는 데이터 타입을 런타임 시에 결정할 수 있다(실제 데이터 타입이 사용하는 시점에 결정).
| 제네릭 변수 | 설명 |
|---|---|
| E | 컬렉션의 요소(element)를 나타내는 제네릭 변수. 주로 List, Set, Queue 등의 컬렉션에서 사용된다. |
| K | 맵(Map)에서 키(key)를 나타내는 제네릭 변수. |
| V | 맵(Map)에서 값(value)을 나타내는 제네릭 변수. |
| T | 제네릭 프로그래밍에서 사용되는 일반 타입 변수. 주로 클래스, 메서드의 반환 타입 또는 매개변수로 사용된다. 두 개 이상 일반 타입이 필요할 땐 S, U 등을 사용한다. |
| N | 제네릭 프로그래밍에서 숫자(number)를 나타내는 제네릭 변수. 주로 숫자형 데이터 타입(int, double, float 등)을 다루는 경우에 사용된다. 일반적으로는 T와 같은 의미로 사용될 수 있다. |
하나 이상의 타입 매개변수를 사용하여 클래스를 정의한 클래스.
public class GenericArray<T> {
private final static int DEFULAT_CAPACITY = 5;
private T[] elements;
private int size = 0;
public GenericArray() {
this(DEFULAT_CAPACITY);
}
public GenericArray(int capacity) {
this.elements = (T[])(new Object[capacity]);
}
public void add(T element) {
if (size == elements.length) {
enhanceCapacity();
}
elements[size++] = element;
}
public T get(int index) {
if (index < 0 || index >= elements.length) {
throw new IndexOutOfBoundsException("범위 초과");
}
return elements[index];
}
public int getSize() {
return this.size;
}
private void enhanceCapacity() {
int newCapacity = size + 1;
elements = Arrays.copyOf(elements, newCapacity);
}
}
public class Main {
public static void main(String[] args) {
GenericArray<String> ga = new GenericArray<>();
ga.add("Hello");
}
}
제네릭 클래스의 인스턴스가 생성될 때 generic 타입의 specific 타입을 받아 그 데이터 타입을 클래스의 변수로 사용한다. 이렇게 구현함으로써 클래스의 멤버 변수, 메서드, 생성자 등에서 사용될 타입을 동적으로 지정할 수 있다.


타입(매개)변수에 타입을 지정하는 것을 제네릭 타입 호출이라고 한다.
선언된 제네릭 클래스를 생성할 때 타입 매개변수는 구체적인 타입으로 대체한다.
하나 이상의 타입 매개변수를 사용하여 메소드를 정의한 메소드.
public class Utils {
public static <T> T getLastElement(T[] array) {
if (array == null || array.length == 0) {
return null;
}
return array[array.length - 1];
}
}
public class Main {
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4, 5};
String[] strArray = {"One", "Two", "Three"};
Integer lastInt = Utils.getLastElement(intArray);
System.out.println("Last Integer: " + lastInt); // Output: Last Integer: 5
String lastStr = Utils.getLastElement(strArray);
System.out.println("Last String: " + lastStr); // Output: Last String: Three
}
}
제네릭 메소드 선언부에 지정한 제네릭 타입은 호출 시에 정해지며, 정해진 구체적인 타입으로 리턴타입이나 파라미터의 타입이 정해진다.