ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템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);
    //    }
    }

    댓글

Designed by Tistory.