-
아이템26. 로 타입은 사용하지 말라.Java/이펙티브 자바 2023. 9. 18. 13:04
핵심 정리 - 제네릭 용어 정리
- 로 타입 : List
- 제네릭 타입 : List<E>
- 매개변수화 타입 : List<String>
- 타입 매개변수 : E
- 실제 타입 매개변수 : String
- 한정적 타입 매개변수 : List<E extends Number>
- 비한정적 와일드카드 타입 : Class<?>
ㆍ아무런 타입이나 다 받는다.
- 한정적 와일드카드 타입 : Class<? extends Annotation>
핵심 정리 - 로(raw) 타입은 사용하지 말라.
: 매개변수화 타입을 사용해야 하는 이유
- 런타임이 아닌 컴파일 타임에 문제를 찾을 수 있다. (안전성)
- 제네릭을 활용하면 이 정보가 주석이 아닌 타입 선언 자체에 녹아든다. (표현력)
- "로 타입"을 사용하면 안정성과 표현력을 잃는다.
- 그렇다면 자바는 "로 타입"을 왜 지원하는가?
- List와 List<Object>의 차이는?
ㆍList는 모든 값이 들어갈 수 있기에 컴파일에러가 발생하지않고 런타임 에러가 발생한다.
// 코드 26-4 런타임에 실패한다. - unsafeAdd 메서드가 로 타입(List)을 사용 (156-157쪽) public class Raw { public static void main(String[] args) { List<String> strings = new ArrayList<>(); unsafeAdd(strings, Integer.valueOf(42)); String s = strings.get(0); // 컴파일러가 자동으로 형변환 코드를 넣어준다. } private static void unsafeAdd(List list, Object o) { list.add(o); } }
- Set과
Set<?>의 차이는?
ㆍ<?> 한종류를 다루는 Set
ㆍ와일드 카드 시 add라는 함수로 값을 넣을 수 없다. (null은 가능) -> 안전성 확보
public class Numbers { static int numElementsInCommon(Set s1, Set s2) { // 해당 컬렉션에 아무거나 넣을 수 있다. int result = 0; for (Object o1 : s1) { if (s2.contains(o1)) { result++; } } return result; } public static void main(String[] args) { System.out.println(Numbers.numElementsInCommon(Set.of(1,2,3), Set.of(1,2))); } }
- 예외 : class 리터럴과 instanceof 연산자
public class UseRawType<E> { private E e; public static void main(String[] args) { // System.out.println(UseRawType<Integer>.class); // 사용 불가능 System.out.println(UseRawType.class); UseRawType<String> stringType = new UseRawType<>(); System.out.println(stringType instanceof UseRawType<String>); // 컴파일은 되지만 소거 된다 System.out.println(stringType instanceof UseRawType); } }
완벽 공략 - GenericRepository
- p156, 마이그레이션 호환성을 위해 로 타입을 지원하고 제네릭 구현에는 소거 방식을 사용하기로 했다. (아이템 28)
- p158, 제네릭 메서드 (아이템 30)
- p158, 한정적 와일드카드 타입 (아이템 31)
- Generic DAO 만들기
public interface Entity { Long getId(); }
public class GenericRepository<E extends Entity> { private Set<E> entities; public GenericRepository() { this.entities = new HashSet<>(); } public Optional<E> findById(Long id) { return entities.stream().filter(a -> a.getId().equals(id)).findAny(); } public void add(E entity) { this.entities.add(entity); } }
public class Account implements Entity { private Long id; private String username; public Account(Long id, String username) { this.id = id; this.username = username; } @Override public Long getId() { return id; } public String getUsername() { return username; } }
public class AccountRespository extends GenericRepository<Account> { // private Set<Account> accounts; // // public AccountRespository() { // this.accounts = new HashSet<>(); // } // // public Optional<Account> findById(Long id) { // return accounts.stream().filter(a -> a.getId().equals(id)).findAny(); // } // // public void add(Account account) { // this.accounts.add(account); // } }
'Java > 이펙티브 자바' 카테고리의 다른 글
아이템28. 배열보다는 리스트를 사용하라 (0) 2023.09.23 아이템 27. 비검사 경고를 제거하라 (0) 2023.09.21 아이템 22 & 아이템 23 & 아이템 24 & 아이템 25 (0) 2023.09.07 아이템21. 인터페이스는 구현하는 쪽에서 생각해 설계하라. (0) 2023.09.05 아이템20. 추상 클래스보다 인터페이스를 우선하라. (0) 2023.08.31