Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- 문서번호
- 0.75px border
- github
- 타입스크립트
- 전역변수
- ES5
- jwt
- 컴포넌튼
- entity
- 으
- Strict
- angular
- es6
- 데이터베이스 #try #이중
- TypeScript
- font-size
- &연산
- npm
- 서버리스 #
- 0.5px border
- 0.25px border
- Websocket
- 당근마켓
- TS
- Props
- 1px border
- 클론코딩
- literal
- 10px
- ZOOM
Archives
- Today
- Total
복잡한뇌구조마냥
[JAVA] Effective Java - 타입 안전 이종 컨테이너를 고려하라(33) 본문
1. 배경: 일반적인 제네릭 컨테이너의 한계
- 제네릭 컬렉션(List<E>, Map<K,V>)은 타입 매개변수의 개수가 고정됨.
- 하지만 여러 타입을 한 컨테이너에 안전하게 담고 싶을 때가 있음.
- 예: DB Row, 설정값 저장소, 어노테이션 API 등
- 단순히 Map<String, Object>를 쓰면 타입 캐스팅 필요 + 타입 안전성 상실.
Map<String, Object> favorites = new HashMap<>();
favorites.put("String", "hello");
favorites.put("Integer", 123);
// 잘못된 타입도 들어감 → 런타임 오류 가능
favorites.put("Integer", "oops");
2. 타입 안전 이종 컨테이너 패턴의 아이디어
- 컨테이너가 아니라 키에 타입 매개변수를 부여하자.
- 즉, Map<Class<?>, Object>를 이용해서 키 자체가 타입 정보를 담도록 설계.
- 이때 Class<T> 같은 키를 **타입 토큰(Type Token)**이라고 부름.
- 제네릭 타입 시스템이 “값의 타입은 키와 같다”는 사실을 보장해줌.
3. 기본 구현
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
// 값 저장
public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(type, instance);
}
// 값 조회
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type)); // 안전한 형변환
}
}
public static void main(String[] args) {
Favorites f = new Favorites();
f.putFavorite(String.class, "hello");
f.putFavorite(Integer.class, 123);
String s = f.getFavorite(String.class); // "hello"
Integer i = f.getFavorite(Integer.class); // 123
✅ 특징
- putFavorite에서 타입 불일치 값은 컴파일 단계에서 차단.
- getFavorite에서 Class.cast()로 런타임 타입 안전성 보장.
- 외부 API는 타입 안정성이 유지되므로 클라이언트 입장에선 캐스팅 불필요.
4. 주의할 점
- 로 타입 Class 객체를 키로 넘기면 타입 안정성이 깨짐 → 항상 Class<T> 형태만 사용해야 함.
- 실체화 불가 타입(non-reifiable type) 예: List<String>은 런타임에 타입 정보가 지워져 사용 불가.
- 악의적인 클라이언트의 잘못된 입력을 막으려면 **동적 형변환 검증(Class.cast)**이 필수.
5. 확장 예시
(1) 직접 정의한 키 타입
- DatabaseRow 같은 컨테이너에서 Column<T>를 키로 쓰면 더 정교한 타입 안정성 제공.
(2) 어노테이션 API
- 자바 리플렉션의 Element.getAnnotation(Class<A>) 메서드는 한정적 타입 토큰을 활용.
- 어노테이션 타입을 키로, 해당 어노테이션 인스턴스를 값으로 → 타입 안전 이종 컨테이너의 한 예.
- asSubclass() 메서드를 활용하면 특정 하위 타입만 허용하는 방식도 가능.
6. 관련 용어 정리
용어 | 의미 | 예시 |
타입 토큰 (Type Token) | 런타임에 타입 정보를 보존하는 키 역할 객체 | String.class, Class<T> |
Class.cast() | 런타임에 안전한 동적 형변환 수행 | String s = String.class.cast(obj) |
실체화 불가 타입 | 런타임에 타입 정보가 사라지는 제네릭 타입 | List<String> |
한정적 타입 토큰 | 특정 상위 타입을 한정한 타입 토큰 | Class<? extends Annotation> |
타입 안전 이종 컨테이너 | 여러 타입을 안전하게 담을 수 있는 컨테이너 | Map<Class<?>, Object> |
7. 결론
컬렉션 API는 타입 매개변수 수가 고정되어 있지만,
컨테이너가 아니라 키를 제네릭으로 만들면 원하는 만큼 다양한 타입을 안전하게 다룰 수 있다.
이를 가능케 하는 패턴이 타입 안전 이종 컨테이너이며, 핵심은 **타입 토큰(Class 객체)**과 **Class.cast()**를 통한 안전한 형변환이다.
LIST
'BE > JAVA' 카테고리의 다른 글
[JAVA] TDD에서 자주 쓰는 테스트 어노테이션 정리 (2) | 2025.08.18 |
---|---|
[JAVA] Effective Java - ordinal 메서드 대신 인스턴스 필드를 사용하라 (35) (1) | 2025.08.17 |
[JAVA] JPA 기초 개념 (0) | 2025.08.14 |
[JAVA] 리플렉션(Reflection) 기초 (0) | 2025.08.13 |
[JAVA] Effective Java - 로 타입(raw type)은 사용하지 말라 (26) (2) | 2025.08.11 |