public class Union {
public static Set union(Set s1, Set s2) {
Set result = new HashSet(s1);
result.addAll(s2);
return result;
}
public static void main(String[] args) {
Set<String> guys = Set.of("톰", "딕", "해리");
Set<Integer> stooges = Set.of(1, 2, 3);
Set<String> all = union(guys, stooges);
// 여기서 ClassCastException 터지게 된다.
for (String o : all) {
System.out.println((String)o);
}
}
}
제네릭 메서드를 사용한 경우
아무런 문제 없이 합칠 수 있고, 꺼내 사용할 수 있다.
public class Union {
// 코드 30-2 제네릭 메서드 (177쪽)
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
Set<E> result = new HashSet<>(s1);
result.addAll(s2);
return result;
}
// 코드 30-3 제네릭 메서드를 활용하는 간단한 프로그램 (177쪽)
public static void main(String[] args) {
Set<String> guys = Set.of("톰", "딕", "해리");
Set<String> stooges = Set.of("래리", "모에", "컬리");
Set<String> aflCio = union(guys, stooges);
System.out.println(aflCio);
}
}
제네릭 싱글턴 팩터리 패턴
싱글턴 팩터리: 싱글턴 객체를 반환하는 팩터리 메소드
제네릭을 사용하면 여러 개의 객체를 만들 필요가 없다.
항등 함수: 들어오는 값과 나가는 값이 같은 함수 (X -> 1 이 들어오면 Y -> 1)
책에 나온 내용
변경 전
public class GenericSingletonFactory {
public static Function<String, String> stringIdentityFunction() {
return (t) -> t;
}
public static Function<Number, Number> integerIdentityFunction() {
return (t) -> t;
}
public static void main(String[] args) {
String[] strings = { "삼베", "대마", "나일론" };
Function<String, String> sameString = stringIdentityFunction();
for (String s : strings)
System.out.println(sameString.apply(s));
Number[] numbers = { 1, 2.0, 3L };
Function<Number, Number> sameNumber = integerIdentityFunction();
for (Number n : numbers)
System.out.println(sameNumber.apply(n));
}
}
변경 후
public class GenericSingletonFactory {
private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;
@SuppressWarnings("unchecked")
public static <T> UnaryOperator<T> identityFunction() {
return (UnaryOperator<T>) IDENTITY_FN;
}
public static void main(String[] args) {
String[] strings = { "삼베", "대마", "나일론" };
UnaryOperator<String> sameString = identityFunction();
// Object 이지만 제네릭을 사용했기 때문에 String으로 형 변환을 해준다.
for (String s : strings)
System.out.println(sameString.apply(s));
Number[] numbers = { 1, 2.0, 3L };
UnaryOperator<Number> sameNumber = identityFunction();
for (Number n : numbers)
System.out.println(sameNumber.apply(n));
}
}
재귀적 타입 한정
재귀적 타입 한정을 이용해 상호 비교할 수 있음을 표현한다.
public class RecursiveTypeBound {
public static <E extends Comparable<E>> E max(Collection<E> c) {
if (c.isEmpty())
throw new IllegalArgumentException("컬렉션이 비어 있습니다.");
E result = null;
for (E e : c)
if (result == null || e.compareTo(result) > 0)
result = Objects.requireNonNull(e);
return result;
}
public static void main(String[] args) {
List<String> argList = List.of("tony", "kang");
System.out.println(max(argList));
}
}
String 내부를 보면 Comparable을 구현하고 있기 때문에 사용이 가능하다.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
...
}
아이템 30. 이왕이면 제네릭 메서드로 만들라.
핵심 정리
예시
제네릭 메서드를 사용하지 않는 경우
ClassCastException
등이 발생할 수 있다.제네릭 메서드를 사용한 경우
제네릭 싱글턴 팩터리 패턴
항등 함수
: 들어오는 값과 나가는 값이 같은 함수 (X -> 1 이 들어오면 Y -> 1)재귀적 타입 한정