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")); // 출력: pear
2. 제네릭 메서드의 이점
타입 안전성 제공 제네릭 메서드는 컴파일 시점에 타입을 체크하여 타입 안전성을 보장합니다. 잘못된 타입을 전달하면 컴파일러가 오류를 검출하므로, 런타임 오류를 줄일 수 있습니다.
코드 중복 제거 및 유지보수성 향상 제네릭 메서드는 여러 타입을 처리할 수 있어 코드 중복을 줄여줍니다. 새로운 타입을 추가할 때 메서드를 새로 작성할 필요가 없으며, 유지보수가 용이해집니다.
타입 캐스팅 제거 제네릭 메서드는 반환 타입을 정확히 알기 때문에, 메서드 호출 시 별도의 타입 캐스팅을 할 필요가 없습니다. 이는 코드의 가독성을 높이고, 실수를 줄이는 데 도움을 줍니다.
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