제네릭스(Generics)란?
제네릭스는 코드의 재사용성과 안정성을 높이는 강력한 도구로, 다양한 데이터 타입을 다루거나 다양한 컬렉션을 사용할 때 매우 유용하다. 이는 컴파일 시에 타입 안정성을 검증하여 런타임 오류를 줄여준다. 제네릭을 사용하면 코드의 가독성도 향상되며, 버그를 찾고 수정하는 과정이 간소화된다.
제네릭 클래스는 하나 이상의 타입 매개변수를 사용하여 클래스를 정의하는 방법이다. 타입 매개변수는 클래스 내의 메소드 및 멤버 변수의 데이터 타입을 지정하는데 사용된다.
public class 제네릭클래스<T> {
private T 데이터;
public 제네릭클래스(T 데이터) {
this.데이터 = 데이터;
}
public T 가져오기() {
return 데이터;
}
}
위의 예제에서 < T >는 타입 매개변수로, 실제 데이터 타입은 사용할 때 결정된다. 이를 통해 다양한 데이터 타입을 지원하는 일반적인 클래스를 생성할 수 있다.
제네릭 메소드는 메소드 내에서 사용할 타입 매개변수를 정의하는 방법이다. 이를 통해 메소드가 다양한 데이터 타입을 처리할 수 있다.
public <T> T 반환하기(T[] 배열) {
if (배열.length > 0) {
return 배열[0];
}
return null;
}
제네릭 클래스 또는 메소드에서 특정 범위 내의 타입만을 허용하려면 제한된 타입 매개변수를 사용할 수 있다. 이를 통해 타입 안정성을 유지하면서 필요한 동작을 수행할 수 있다.
public <T extends Number> double 평균(T[] 숫자들) {
double 합계 = 0.0;
for (T 숫자 : 숫자들) {
합계 += 숫자.doubleValue();
}
return 합계 / 숫자들.length;
}
와일드카드('?')는 제네릭 타입의 일부 제약을 피하기 위해 사용된다. 와일드카드를 사용하면 다양한 타입의 데이터를 처리할 수 있으며, 와일드카드 상한(? extends T) 및 하한(? super T)을 사용하여 범위를 제한할 수 있다.
public void 처리하기(List<? extends Number> 숫자목록) {
for (Number 숫자 : 숫자목록) {
// 처리 로직
}
}
자바는 제네릭을 활용한 다양한 컬렉션 클래스를 제공한다. 예를 들어, ArrayList< T>, HashMap<K, V> 등의 컬렉션은 제네릭을 사용하여 다양한 데이터 타입을 저장할 수 있다.
List<String> 문자열목록 = new ArrayList<>();
문자열목록.add("첫 번째");
문자열목록.add("두 번째");
제네릭스(Generics) 추가 설명
제네릭 타입은 컴파일 시에 타입 안전성을 제공하지만, 런타임에서는 타입 정보가 소거 된다. 이것을 "타입 소거"라고 하며 타입 소거는 호환성을 유지하기 위해 이전 버전의 자바와의 하위 호환성을 지원하고, 제네릭스를 사용하지 않는 코드와의 상호 작용을 가능하게 한다.
예를 들어, List< String>과 List< Integer>은 컴파일 시에 다르게 처리되지만 런타임에서는 모두 List로 타입 소거된다.
타입 소거로 인해 로타입 이라고 불리는 타입이 생긴다. 로타입은 제네릭 타입에서 타입 매개변수를 생략한 형태이다. 로타입은 주로 제네릭스를 사용하지 않는 레거시코드와의 상호 작용을 위해 사용된다.
예를 들어, List는 로타입이며, List< String>과 같은 제네릭 타입의 일반 버전이다.
제네릭 코드와 비제네릭 코드 간의 상호 작용은 타입 안정성을 위해 경계 조건이 필요하다. 제네릭 코드에서 비제네릭 코드로 데이터를 전달할 때 경고나 캐스트 연산이 필요할 수 있다.
List<String> 문자열목록 = new ArrayList<>();
문자열목록.add("첫 번째");
// 비제네릭 메소드에 전달할 때 캐스트 연산이 필요
String 값 = (String) 문자열목록.get(0);
와일드카드('?')는 제네릭 코드에서 유연성을 제공한다. 와일드카드를 사용하면 다양한 제네릭 타입의 객체를 다루기 쉬워진다. 상한 와일드카드(? extends T) 및 하한 와일드카드(? super T)를 사용하여 범위를 제한할 수 있다.
public void 처리하기(List<? extends Number> 숫자목록) {
for (Number 숫자 : 숫자목록) {
// 처리 로직
}
}