제네릭은 특정 타입에 의존하지 않고 다양한 타입을 처리할 수 있는 기능입니다. 컴파일 단계에서 타입 안정성을 확보할 수 있어, 런타임 오류를 줄이는 데 도움을 줍니다.
코드 재사용성 증가
다양한 데이터 타입을 하나의 코드로 처리 가능
타입 안정성 유지
컴파일 시점에서 타입 검사로 오류 방지
가독성 향상
타입이 명확하게 정의되어 코드 이해도 증가
T - Type (일반적인 타입)
E - Element (요소)
K - Key (키)
V - Value (값)
N - Number (숫자)
// 제네릭 클래스
public class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
// 사용 예시
Box<Integer> intBox = new Box<>();
intBox.setValue(123);
System.out.println(intBox.getValue()); // 123
Box<String> strBox = new Box<>();
strBox.setValue("Hello");
System.out.println(strBox.getValue()); // Hello
// 상한 제한 와일드카드
public void processNumbers(List<? extends Number> numbers) {
// Number의 하위 타입만 허용
}
// 하한 제한 와일드카드
public void addNumbers(List<? super Integer> numbers) {
// Integer의 상위 타입만 허용
}
// 비한정적 와일드카드
public void printList(List<?> list) {
// 모든 타입 허용
}
public class Util {
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
// Repository 계층
public interface GenericRepository<T, ID> extends JpaRepository<T, ID> {
// 공통된 메서드 정의
}
// Service 계층
public class UserService<T> {
private final GenericRepository<T, Long> repository;
public UserService(GenericRepository<T, Long> repository) {
this.repository = repository;
}
public T findById(Long id) {
return repository.findById(id).orElse(null);
}
}
// ResponseEntity 사용
public ResponseEntity<List<UserDto>> getUsers() {
List<UserDto> users = userService.findAll();
return ResponseEntity.ok(users);
}
// Optional 사용
public Optional<User> findById(Long id) {
return userRepository.findById(id);
}
// Stream API 활용
public List<UserDto> getUserDtos(List<User> users) {
return users.stream()
.map(UserDto::from)
.collect(Collectors.toList());
}
// 제네릭 배열 생성 불가
T[] array = new T[10]; // 컴파일 에러
// 올바른 방법
T[] array = (T[]) new Object[10];
// 제네릭 타입 소거
// 컴파일 후에는 제네릭 타입 정보가 사라지고 Object로 변환됨
정리
제네릭은 Java와 Spring에서 타입 안정성과 코드 재사용성을 높이는 핵심 기능입니다. 특히 Spring의 Repository나 Service 계층에서 다양한 엔티티를 처리할 때 유용하게 활용됩니다. 다만, 제네릭 타입 소거나 배열 생성 제한 같은 특징들을 잘 이해하고 사용해야 합니다.
이러한 제네릭의 이해와 활용은 견고하고 유지보수가 쉬운 코드를 작성하는 데 큰 도움이 됩니다.