Effective Java 3/E - (5) Generic

신복호·2020년 12월 6일
0

Effactive JAVA 3/E

목록 보기
5/12
post-thumbnail

5장 Generic

제네릭은 JAVA 5에서 부터 사용할 수 있다. 제네릭이 지원되기 전까지는 컬랙션에서 객체를 꺼낼때마다 형변환을 해줘야 했다.
그래서 엉뚱한 타입의 객체를 넣어주면 런타임에 형변환 오류가 나곤 했다. 반면 제네릭을 사용하면 컬랙션이 담을 수 있는 타입을 컴파일러에 알려주게 된다.
그래서 컴파일러는 알아서 형변환 코드를 추가할 수 있게 되고,
엉뚱한 타입의 객체를 넣으려는 시도를 컴파일 과정에서 차단한다.

아이템 26. Row type은 사용하지 말라

Row Type?

제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을때를 말한다.

Generic 클래스(인터페이스)?

클래스/인터페이스 선언에 타입 매개변수가 쓰이는 것
-> ex) List[E]
-> 가각의 제네럴 타입은 일련의 매개변수화 타입을 정의한다.

Row Type 단점

  • Row Type 을 사용하게 되면 제네릭이 안겨주는 안정성과 표현력을 모두 잃게 된다.

  • 또한 런타임에 예외가 발생할수 있다.

  • 즉 제네릭이 도입되기 이전 코드와의 호환성을 위해서 제공될 뿐이다.

아이템 27. 비검사 경고를 제공하라

제네릭을 사용하기 시작하면 수많은 컴파일러 경고를 보게 될것이다.

  • ex) 비검사 형변환 경고, 비검사 메소드 호출 경고, 가변인수 타입 경고, 비검사 변환 경고 등
  • 할수 있는 한 모든 비검사 경고를 제거하는것이 좋다. (안정성 보장)
  • 경고를 제거할 수는 없지만 타입이 안전하다고 확신을 할수 있다면 @SuppressWarning("unchecked")를 사용하자
  • @SuppressWarning("unchecked")는 항상 가능한 좁은 범위에 적용해보자 (변수선언, 짧은 메소드, 생성자...)
  • @SuppressWarning("unchecked")를 사용할 때에는 그 경고를 무시해도 되는 이유를 항상 주석으로 남겨놓아야 한다.

할수 있는한 모든 비검사 경고를 제거하는것이 좋다.

아이템 28. 배열 보다는 List를 사용하자.

배열과 제네릭 타입에는 중요한 두가지 차이가 있다.

  • 배열은 공변(covariant; 共變)이다. 반면 제네릭은 불공변(incovariant; 不共變) 이다.

    Object[] array = new Long[1];
    array[0] = "들어갈수 있을까"; // 컴파일은 되지만, 런타임시 오류 발생
    
    List<Object> list = new ArrayList<Long>();
    list.add("들어갈수 있을까"); // 컴파일 조차 되지 않는다.
  • 배열은 실체화(reify)가 된다.

    -> 배열은 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인하다. Long 배열에 String을 넣으면 ArrayStoreException이 발생한다.

    -> 형 안정성을 보장 받지 못함

  • 제네릭은 타입 정보가 런타임에는 소거가 된다.

    -> 원소 타입을 컴파일 타임에만 검사하며 런타임에서는 알수도 없다.

    -> 소거는 제네릭이 지원되기 전의 레거시 코드와 제네릭 타입을 함께 사용할 수 있게 해주는 메커니즘

컴파일 오류나 경고를 만난다면 배열을 리스트로 대체하는 방법을 적용하자.

아이템 29. 이왕이면 제네릭 타입으로 만들라

클라이언트에서 직접 형변환을 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다.
그러니 새로운 타입을 설계 할때는 형변환 없이도 사용할 수 있도록 해라, 그렇게 하려면 제네릭 타입으로 만들어야 하는 경우가 많다. 기존 클라이언트에서 영향을 주지 않으면서 새로운 사용자를 훨씬 편하게 해주는 길이다.

아이템 30. 이왕이면 제네릭 메소드로 만들라

제네릭 타입과 마찬가지로, 클라이언트에서 입력 매개변수와 반환값을 명시적으로 형변환을 해야하는 메소드 보다 제네릭 메소드가 더 안전하며 사용하기도 쉽다.
타입과 마찬가지로 메소드도 형변환 없이 사용할 수 있는 편이 좋으며, 많은 경우 그렇게 하려면 제네릭 메소드가 되어야 한다.

아이템 31. 한정적 와일드카드를 사용해 API 유연성을 높이라 (수정 필요)

  • 유연성을 극대화 하려면 원소의 생산자나 소비자용 입력 매개변수에 와일드카드 타입을 사용하는 것이 좋다.
  • 단. 반환타입에는 한정적 와일드카드 타입을 사용하면 안된다.
  • 즉 조금 복잡하더라도 와일드카드 타입을 적용하면 API가 유연해지나 널리 쓰이는 라이브러리를 작성할 경우 와일드 카드를 적절하게 사용해야 한다 -> PECS( 생산자[producer] 는 extends를 소비자[consumer]는 super를 사용한다.

조금 복잡하더라도 와일드카드 타입을 적용하면 API가 훨씬 유연해진다.

아이템 32. 제네릭과 가변인수를 함께 쓸 때에는 신중하라

  • 가변인수: 필요에 따라 메게변수(인수)를 조절하는 기술
  • 가변인수 기능은 배열을 노출하여 추상화가 완벽하지 못하고, 배열과 제네릭 타입 규칙이 서로 다름, 제네릭 varargs 매개변수는 타입 안전하지 않지만 허용된다.
  • 메소드에 제네릭(혹은 메게변수화된) varargs 매개변수를 사용하고자 한다면, 먼저 그 메소드가 타입이 안전한지 확인한 다음에 @SafeVarangs 어노테이션을 달아 사용하는데 불편함이 없게 하자

가변인수와 제네릭은 잘 어울리지 않는다.

아이템 33. 타입 안전 이종 컨테이너를 고려하라 (수정 필요)

  • 컬랙션 API로 대표되는 일반적인 제네릭 형태에서는 한 컨테이너가 다룰수 있는 타입 매개변수의 수가 고정이 되어있다.
  • 그러나 컨테이너 자체가 아닌 키를 타입 매개변수로 바꾸면 이런 제약 없는 타입 안전 이종 컨테이너를 만들수 있다. 타입 안전 이종 컨테이너는 class를 키로쓰며 이런식으로 쓰이는 class 객체를 타입 토큰이라고 한다.
  • 예) 데이터베이스 행을 표현한 DatabaseROw 타입에는 제네릭 타입인 Column[T]을 키로 쓸수 있다
profile
한참 열정이 가득한 백엔드 개발자입니다.

0개의 댓글