Item30. Favor Generic Methods
이왕이면 제네릭 메서드로 만들라
자바에서 제네릭 메서드는 메서드의 동작을 호출 시점에 지정된 타입으로 일반화할 수 있게 해줍니다. 제네릭 메서드를 사용하면 특정 타입에 종속되지 않고 다양한 타입을 처리할 수 있어 코드의 재사용성을 높이고, 타입 안전성을 확보할 수 있습니다.
1. 제네릭 메서드의 필요성
제네릭 메서드는 타입 매개변수를 받아들여, 메서드 호출 시점에 타입을 결정하게 합니다. 이를 통해 코드 중복을 줄이고, 타입에 독립적인 메서드를 작성할 수 있습니다.
1.1. 제네릭 메서드를 사용하지 않는 예시
제네릭을 사용하지 않은 메서드는 여러 타입을 처리하기 위해 오버로딩을 사용하거나, Object 타입을 사용하여 타입을 통일하는 경우가 많습니다.
//제네릭 메서드를 사용하지 않은 경우의 예시
public class Utils {
// 서로 다른 타입의 두 값을 비교해 더 큰 값을 반환하는 메서드
public static int max(int a, int b) {
return (a > b) ? a : b;
}
public static double max(double a, double b) {
return (a > b) ? a : b;
}
}위 코드에서는 max 메서드가 두 개의 다른 타입(int, double)을 처리하기 위해 각각 메서드를 오버로딩하고 있습니다. 이는 코드의 중복을 증가시키고, 새로운 타입을 추가할 때마다 새로운 메서드를 작성해야 합니다.
1.2. 제네릭 메서드를 사용한 예시
제네릭 메서드를 사용하면 타입에 의존하지 않고, 더 간결하고 재사용 가능한 코드를 작성할 수 있습니다.
//제네릭 메서드를 사용한 max 메서드의 개선된 예시
public class Utils {
// 제네릭 메서드를 사용하여 타입에 독립적인 max 메서드 작성
public static <T extends Comparable<T>> T max(T a, T b) {
return (a.compareTo(b) > 0) ? a : b;
}
}<T extends Comparable<T>>: 제네릭 타입 매개변수 T는 Comparable 인터페이스를 구현해야 함을 명시하여, compareTo 메서드를 사용할 수 있도록 보장합니다.
이렇게 작성된 제네릭 메서드는 int, double, String 등 서로 다른 타입에 대해 작동할 수 있습니다.
// 사용 예시
System.out.println(Utils.max(10, 20)); // 출력: 20
System.out.println(Utils.max(5.5, 2.2)); // 출력: 5.5
System.out.println(Utils.max("apple", "pear")); // 출력: pear2. 제네릭 메서드의 이점
타입 안전성 제공 제네릭 메서드는 컴파일 시점에 타입을 체크하여 타입 안전성을 보장합니다. 잘못된 타입을 전달하면 컴파일러가 오류를 검출하므로, 런타임 오류를 줄일 수 있습니다.
코드 중복 제거 및 유지보수성 향상 제네릭 메서드는 여러 타입을 처리할 수 있어 코드 중복을 줄여줍니다. 새로운 타입을 추가할 때 메서드를 새로 작성할 필요가 없으며, 유지보수가 용이해집니다.
타입 캐스팅 제거 제네릭 메서드는 반환 타입을 정확히 알기 때문에, 메서드 호출 시 별도의 타입 캐스팅을 할 필요가 없습니다. 이는 코드의 가독성을 높이고, 실수를 줄이는 데 도움을 줍니다.
3. 제네릭 메서드 작성 시 고려 사항
3.1. 제네릭 타입 매개변수의 위치
제네릭 메서드의 타입 매개변수는 반환 타입 앞에 위치해야 합니다. 예를 들어, <T> T methodName(T arg)와 같이 정의해야 합니다.
// 올바른 제네릭 메서드 정의
public static <T> T identity(T arg) {
return arg;
}3.2. 제네릭 타입의 제한
제네릭 타입을 제한하려면 extends 키워드를 사용하여 특정 타입이나 인터페이스를 구현하도록 제약할 수 있습니다. 예를 들어, T extends Number는 제네릭 타입 T가 Number의 하위 타입이어야 함을 의미합니다.
// 제네릭 타입 매개변수를 Number의 하위 타입으로 제한
public static <T extends Number> double sum(T a, T b) {
return a.doubleValue() + b.doubleValue();
}3.3. 와일드카드와 제네릭 메서드
와일드카드(?)는 제네릭 타입을 불특정하게 사용하고자 할 때 유용합니다. 메서드 매개변수에 와일드카드를 사용하면, 호출 시점에 타입이 정해지지 않은 다양한 제네릭 타입을 처리할 수 있습니다.
// 와일드카드를 사용하여 제네릭 타입의 리스트 요소 출력
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}4. 제네릭 메서드의 예시: 자바 컬렉션 프레임워크
Collections 클래스의 다양한 메서드는 제네릭을 사용하여 타입 안전성과 유연성을 제공합니다.
4.1. Collections의 binarySearch
binarySearch 메서드는 리스트에서 이진 검색을 수행하는 제네릭 메서드입니다.
public static <T extends Comparable<? super T>> int binarySearch(List<T> list, T key);<T extends Comparable<? super T>>: 제네릭 타입T가Comparable인터페이스를 구현해야 하며, 이는T또는 그 상위 타입과 비교할 수 있어야 함을 의미합니다.이 메서드는 어떤 타입의 리스트에도 사용될 수 있으며, 타입 안전한 검색을 제공합니다.
4.2. Collections의 copy
copy 메서드는 한 리스트에서 다른 리스트로 요소를 복사하는 제네릭 메서드입니다.
public static <T> void copy(List<? super T> dest, List<? extends T> src);<T>: 제네릭 타입 매개변수.List<? super T> dest: 제네릭 타입T또는 그 상위 타입의 요소를 받을 수 있는 리스트.List<? extends T> src: 제네릭 타입T또는 그 하위 타입의 요소를 가진 리스트.
Last updated