Generic method와 주의사항

박근수·2024년 11월 27일
0

01. 제네릭 메서드로 만들어라

Type safe한 Generic Method

List<String> stringList = List.of("T1","T2","T3");

static<E> List<E> of (E e1, E e2, E e3) {
	return new ImmutableCollections.ListN<>(e1, e2, e3);
}

private static UnaryOperator<Object> IDENTIFY_FN = (t) -> t;

public static<T> UnaryOperator<T> identityFunction(){
	return (UnaryOperator<T>) IDENTIFY_FN; (O)
	return IDENTIFY_FN; (X)
    //object는 제레닉으로 캐스팅죄디 않음. 유형이 다르기 때문에 에러발생
}

Type 한정

Method의 parameter 타입을, interface의 Type으로 한정한다.

interface Comparable<t>{
	int compare(T o);
}

genric 타입과 같이 형변환 해야하는 method보다 generic method가 더 안전하고, 심지어 사용허기도 쉽다. (형변환 해야하는 메서드는 generic으로 만들자)

02. 한정적 와일드카드를 사용해 API 유연성을 높여라

Bounded Wildcard 설명 예제 코드

public class Stack<E>{
	public static final int DEFAULT_SIZE = 20;
    private int size;
    private E[] elements;
    public Stack(){ elements = (E[]) new Object[DEFAULT_SIZE]; size = 0;}
    
    public E pusg(E item){
    	elements[++size] = item;
        return item;
    }
    
    public void pushAll(Iterable<E> src){
    	for (E e : src){
        	push(e);
        }
    }
}


//컴파일 에러 : 불공변이기 때문에 (invariant) 자가 타입만 허용
Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers = List.of(1,2);
numberStack.pushAll(integers);

해결책

//제네릭 <E> 를 extends한 와일드카드로 입력
public void pushAll(Iterable<? extends E> src){
	for (E e: src)
    	push(e);
}

03. 제네릭과 가변인수를 함께 쓸 떄는 신중하라

Variadic Arguments (가변 인수)

Mehtod의 arguments의 개수를 클라이언트가 조절할 수있게 한다.
또한 반드시 한 개의 가변 인수만을 사용해야 하며 맨 마지막 Arguments로 사용해야 한다.

static void mergeAll(List<String>.. stringList){}
//위의 코드는 컴파일 불가, 가능하게하려면
static void mergeAll(List<String> one, List<String, two){
	List<String>[] stringLists = {one, two};
}

Heap Pollution(오염)

List[] test = {List.of(1), List.of(2)}; // True
List<Integer>[] test2 = {List.of(1), List.of(2)}; // Error

안전하기 위해선 제네릭 배열에 아무것도 저장하거나 덮어쓰지 말고, 배열의 참조를 밖으로 노출시키지 말아야 한다.

Remove Warning

  • SuppressWarnings (컴파일 경고 숨기기)
  • SafeVarargs (메서드의 타입 안정성을 보장함)

주의 사항

제네릭 배열에 아무것도 저장하거나 덮어쓰지말고, 배열의 참조를 밖으로 노출시키지 말아야 한다.
제네릭과 가변인수를 함께 사용할 때에는 궁합이 잘 맞지 않으니 조심하자

03. 타입 안정 이종 컨테이너를 고려하라

타입 안정 이종 컨테이너

//Key 가 wildcard Type
public class Favorites{
	private Map<Class<?>, Object> favorites = new HashMap<>();
    public <T> void putFavorite<Class<T> type, T instance){
    	favorites.put(Objects.requireNonNull(type), instance);
    }
    public <T> T getFavorite(Class<T> type){
    	return type.cast(favorites.get(type));
    }
}
//Get할 때 요청받은 타입의 value를 찾아 cast 하여 response함

동적 형 변환

public <T> void putFavorite(Class <T> type, T instance){
	favorites.put(Objects.requireNonNull(type), type.cast(instance));
}

Favorites favorites = new Favorites();
Game game = favorites.putFavorite(Game.class, new Game());

//HashSet<Integer>에 string을 넣는 것 같은 문제를 막을 수 있다.

슈퍼 타입 토큰 (ParameterizedTypeReference)

//TypeReference, ParameterizedTypeRefernece 등이 있다.
RestTemplate re = enw RestTemplate();
List<String> rest = re.exchange("http://localhost:8080", HttpMethod.GET, null, new ParameterizedTypeReference<List<String>>()
{}).getBodt();
profile
개발블로그

0개의 댓글