ThreadLocal<T>
, AtomicReference<T>
등이 있습니다.Set<Integer>
, Map<String, Integer>
와 같이 제네릭을 사용하여 타입을 명시합니다.
Set<Integer>
타입은 자신은 Integer
타입을 담을 수 있는 Set
인터페이스이다 라는 것을 의미합니다.Set<? extends Exploded>
의 경우는 폭발물 분류인 물건들을 담는 컨테이너라고 생각할 수 있습니다.위의 컨테이너들도 좋지만 때로는 더 유연한 수단이 필요한 경우도 있습니다.
예를 들어 DB에서 값을 가져오는 경우를 생각할 수 있습니다.
USER
라는 테이블에서 데이터를 가져와야 한다고 생각해 봅니다. 이때 USER
테이블은 다음과 같은 데이터를 가지고 있습니다.CREATE TABLE USER (
id BIGINT PRIMARY KEY AUTO INCREMENT,
name VARCHAR(20) NOT NULL,
age INT NOT NULL,
create_at DATETIME NOT NULL,
modified_at DATETIME NOT NULL
);
SELECT id, name, age
FROM USER
WHERE id = 10;
id
는 long
으로, name
은 String
으로, age
는 int
로 가져와야할 것입니다.위와 같은 상황에서 좋은 방법이 존재합니다.
예시를 함께 보도록 합시다. 아래는 타입별로 즐겨 찾는 인스턴스를 저장하고 가져올 수 있는 Favorite 클래스입니다.
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));
}
}
public class Main {
public static void main(String[] args) {
Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
f.putFavorite(Integer.class, 12345);
f.putFavorite(Class.class, Favorites.class);
String str = f.getFavorite(String.class);
int favoriteInteger = f.getFavorite(Integer.class);
Class<?> favoriteClass = f.getFavorite(Class.class);
System.out.println(str + " " + favoriteInteger + " " + favoriteClass);
}
}
putFavorite
메서드는 클래스 타입과 특정 타입의 인수를 받습니다.
Class<T> type
은 인자로 들어오는 클래스 타입을 의미하며 인자로 들어오는 클래스 타입과 동일한 타입의 instance
가 들어오게 됩니다.이렇게 여러 타입들을 저장하고 가져올 수 있는 컨테이너를 이종 컨테이너라고 하며 제네릭을 사용하여 타입 안정성을 보장하므로 타입 안전 이종 컨테이너라고 부릅니다.
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));
}
}
Map
의 Class<?>
가 Key로 들어간다는 것입니다.
Class<?>
타입이 아닌 그냥 비한정적 와일드카드인 ?를 넣으면 어떻게 될까요? 이렇게 된다면 Key에는 null
만 올 수 있을 것입니다.Map<?, Object>
에서 어떤 타입의 Map
이 올 지 알 수 없기 때문에 타입이 존재하는 값을 넣을 수 없기 때문입니다. 따라서 이를 해결하기 위해서는 Key를 ? extends Something
과 같이 제한을 해줘야 할 것입니다.
Value
가 Object
인 것도 주목해야 합니다.
Value
가 Object
이기 때문에 favorites
는 키와 값 사이의 타입 관계를 Map
자체적으로는 보증하지 않습니다.
type
은 Integer.class
로 들어가지만 Value는 String
타입의 데이터가 들어갈 수 있기 때문입니다.putFavorite
메서드를 통해서 제네릭이 키와 값이 동일한 타입임을 보증할 수 있습니다. 따라서 제네릭 시스템으로 타입 안정성을 가져가는 동시에 값이 Object
로 어떤 값이든 들어갈 수 있는 유연성을 동시에 누릴 수 있습니다.getFavorite
메서드를 보면 cast()
메서드가 존재합니다. cast()
메서드는 다음과 같은 내부 구현을 가지고 있습니다.