1. 코드 재사용성
Generics를 사용하면 다양한 타입에서 동작하는 클래스를 하나의 일반화된 코드로 작성할 수 있습니다.
- 동일한 로직을 구현하기 위해 여러 클래스나 메서드를 작성할 필요가 없습니다.
Generic을 사용하지 않은 경우
class StringBox {
private String value;
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
class IntegerBox {
private Integer value;
public void setValue(Integer value) {
this.value = value;
}
public Integer getValue() {
return value;
}
}
위처럼 타입별로 클래스를 작성하는 대신, Generic을 사용하면 하나의 클래스로 해결할 수 있습니다:
Generic을 사용한 경우
class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
이제 Box를 여러 타입에 대해 재사용할 수 있습니다:
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
Box<Integer> intBox = new Box<>();
intBox.setValue(123);
2. 타입 안정성 (Type Safety)
Generics를 사용하면 컴파일 시 타입 체크를 강제하여, 런타임에 발생할 수 있는 ClassCastException을 방지합니다.
Generic을 사용하지 않은 경우
List list = new ArrayList();
list.add("Hello");
list.add(123); // 의도하지 않은 값 추가
String str = (String) list.get(1); // 런타임에 ClassCastException 발생
Generic을 사용한 경우
List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123); // 컴파일 에러 발생
String str = list.get(0); // 안전한 타입 반환
Generic을 사용하면 다른 타입의 데이터가 잘못 추가되는 것을 방지하여, 런타임 오류 대신 컴파일 타임에 오류를 잡을 수 있습니다.
3. 컴파일 타임 타입 체크
Generic을 사용하면 컴파일러가 타입을 체크하므로, 개발 중 타입 관련 오류를 발견하기 쉽습니다.
이는 런타임 오류를 줄이고, 디버깅 시간을 절약합니다.
// Generic 사용 시
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add("Hello"); // 컴파일 에러: String은 Integer 타입이 아님
Generic이 없으면 런타임에서야 오류를 알게 됩니다. 하지만 Generic을 사용하면 컴파일러가 잘못된 타입을 미리 감지합니다.
4. 가독성 및 유지보수성 향상
Generic을 사용하면 타입 변환(Casting)을 명시적으로 하지 않아도 되므로 코드가 간결해지고, 가독성과 유지보수성이 향상됩니다.
Generic 없이 타입 변환
List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0); // 명시적 타입 변환 필요
Generic을 사용한 경우
List<String> list = new ArrayList<>();
list.add("Hello");
String str = list.get(0); // 타입 변환 불필요
5. 제네릭 메서드를 통한 유연한 설계
Generic은 메서드 수준에서도 사용할 수 있어 더욱 유연한 설계를 가능하게 합니다.
제네릭 메서드
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3};
String[] strArray = {"A", "B", "C"};
printArray(intArray);
printArray(strArray);
}
위의 printArray 메서드는 다양한 타입의 배열에 대해 동작할 수 있습니다.
6. 와일드카드로 유연성 제공
Generic은 와일드카드(?)를 통해 특정 범위의 타입을 처리하거나 제한할 수 있습니다.
와일드카드 사용
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
범위 제한
public static void printNumbers(List<? extends Number> list) {
for (Number num : list) {
System.out.println(num);
}
}
결론
- 코드 재사용성: 다양한 타입을 하나의 코드로 처리 가능.
- 타입 안정성: 컴파일 시 타입 체크를 통해 런타임 오류 방지.
- 코드 가독성: 타입 변환을 줄여 코드 간결화.
- 유지보수성 향상: 타입 안정성을 통해 오류 수정 시간 단축.
'IT 개발 라이프 > Back_End' 카테고리의 다른 글
자바 HashMap vs HashTable vs ConcurrentHashMap (1) | 2024.12.31 |
---|---|
자바(JAVA) try-with-resources (0) | 2024.12.31 |
@Component와 @Configuration란? (1) | 2024.12.26 |
자바(Java)의 메모리 영역 (1) | 2024.12.19 |
JRE, JDK, JVM: 자바의 핵심 구성 요소 🚀 (1) | 2024.12.18 |