정리
타입 안정성을 지키고 컴파일 에러를 보장하기 위해 로 타입이 아닌 제네릭 타입을 사용하라!
제네릭 타입이란?
클래스/인터페이스에 <타입 매개변수>를 붙인 타입을 의미한다. 제네릭 클래스는 클래스<타입 매개변수>, 제네릭 인터페이스는 인터페이스<타입 매개변수> 형태이다.
- 제네릭 예시
- List<E> → E는 정규 타입 매개변수
- List<String> → String은 실제 타입 매개변수
제네릭 타입 사용 시 이점
private final Collection<Stamp> stamps = ...;
- 타입 안정성
- 컴파일러가 stamps에는 Stamp 인스턴스만 넣어야함을 인지함
- stamps에 다른 타입의 인스턴스를 넣으려할 때 컴파일 에러 발생
- 표현력
로 타입
List와 같이 타입 매개변수가 없는 제네릭 타입을 의미
- 로 타입 사용을 하면 안되는 이유
- 타입 안정성과 표현력이 없음
- 런타임 에러 가능성 존재
- 예시 코드
- 에러 발견 시기가 컴파일 시점이 아닌 런타임 시점이다.
// 로 타입 private final Collection stamps = {...}; stamps.add(new Coin(...)); // 위 코드에서 발생할 수 있는 문제점 add() 하는 시점에는 에러가 발생하지 않지만 추후에 런타임 에러 가능성이 있고, 원인 코드를 추적하기가 어려워짐 // 해결책 private final Collection<Stamp> stamps = {...}; -> 제네릭으로 타입 안정성 확보 stamps.add(new Coin(...)); -> 컴파일 에러 발생
로 타입이 만들어진 이유
제네릭이 나오기 전 코드와의 호환성을 위해 로 타입이 만들어졌다.
List vs. List<Object>
- List
- 제네릭 타입을 배제한다는 것을 의미
- List 타입 매개변수에 List<String> 넘길 수 있음 → 타입 안정성 보장되지 않음
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
// List 에 List<String> 넘길 수 있음
unsafeAdd(strings, Integer.valueOf(42));
strings.get(0); // 컴파일러가 자동 형변환
}
// 매개변수에 로 타입 List 존재
private static void unsafeAdd(List list, Object o) {
list.add(o);
}
- List<Object>
- 모든 타입(Object)을 허용한다는 의사를 컴파일러에 명확히 전달한다는 것을 의미
- List<Object> 타입 매개변수에 List<String> 넘길 수 없음 → 타입 안정성 보장, 무공변성?
- List<Object>, List<String>
public static void main(String[] args) { List<String> strings = new ArrayList<>(); // List<Object> 에 List<String> 넘길 수 없음 safeAdd(strings, Integer.valueOf(42)); -> 컴파일 에러 발생 } // 매개변수에 제네릭 타입 List<Object> 존재 private static void safeAdd(List<Object> list, Object o) { list.add(o); }
→ 로 타입 말고 제네릭 타입 사용해라
비한정적 와일드 카드 <?>
제네릭 타입을 쓰고 싶지만, 실제 타입 매개변수가 무엇인지 신경 쓰고 싶지 않을 때 사용하는 타입이다.
Set<?>과 같은 형태로 사용하면 된다. 비한정적 와일드 카드를 사용하면 타입 안정성을 보장하고, 유연한 코드를 작성할 수 있다.
로 타입을 사용하는 예외 케이스
- class 리터럴에는 로 타입을 사용하라
- List.class, String[].class, int.class 사용 가능
- List<String>.class, List<?>.class 사용 불가능
Class<List> listClass = List.class; // 가능 List<?>.class; // 불가능
- instanceof 연산자
- 런타임에는 제네릭 타입 정보가 지워지므로 <?>(비한정적 와일드카드 타입) 외에 매개변수 타입은 instanceof 사용에 적용할 수 없다.
if (o instanceof Set) { // 로 타입 Set<?> s = (Set<?>) o; // (로 타입 -> 와일드 카드 타입) 형변환 }
'Dev Language > EffectiveJava' 카테고리의 다른 글
[EffectiveJava] 이왕이면 제네릭 메서드로 만들라 (0) | 2025.01.11 |
---|---|
[EffectiveJava]이왕이면 제네릭 타입으로 만들라 (0) | 2025.01.05 |
[EffectiveJava] 멤버 클래스는 되도록 static으로 만들라 (0) | 2024.12.22 |
[EffectiveJava] 인터페이스는 구현하는 쪽을 생각해 설계하라 (1) | 2024.12.08 |
[Effective Java] 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2023.11.09 |