effective java 5장 제네릭

bluesky·2023년 12월 12일

목차

5장 제네릭

아이템 26. 로 타입은 사용하지 말라

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

아이템 28. 배열보다는 리스트를 사용하라

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

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

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

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

아이템 33. 타입 안전 이종 컨테이너를 고려하라

정리 방식.

  • 다 정리 NO
  • 새롭게 알게 된 거나, 정리가 필요한것, 더 찾아본것 위주로!

아이템 26. 로 타입은 사용하지 말라

  • 로타입은 List list; 이렇게 쓰는것
    • 로타입이 아닌것은 List list; 이렇게 쓰는것.
    • 로타입으로 쓰면 컴파일 타임에 타입을 검사할수 없는 단점.

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

  • 비검사 경고란?
    • 검사를 해야하는 데 하지 않아서 경고하는 것.
    • 그래서 그런 경고들로 제네릭과 관련된 비검사 경고가 있을수 있는데 그것을 적절히 조치하여(로타입을 → 제네릭으로 변경) 작업하라는 뜻.

아이템 28. 배열보다는 리스트를 사용하라

배열 VS 제네릭 (공변)

배열은 공변(covariant)이다. 즉, Sub 클래스가 Super 라는 클래스의 하위 타입이라면, 배열 Sub[]은 배열 Super[]의 하위 타입이 된다. 이것을 공변이라고 한다. 하지만 제네릭 불공변(invariant)이다. 서로 다른 Type1과 Type2가 있을 때, List<Type1>은 List<Type2>의 상위 타입도 하위 타입도 아니다.??

  • 제네릭은 배열과 호환성이 안좋음.
  • 배열보다 리스트를 사용하면 컴파일 시점에 오류를 확인할 수 있다.
  • 그런니 리스트를 써라!!

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

  • 아까 말했듯이.. 이왕이면 로타입 말고 제네릭으로!
  • 제네릭 변경 방법 다음은 제네릭 클래스로 변경하는 과정이다. 먼저,  (1) 클래스 선언에 타입 매개 변수를 추가한다. 그리고  (2) 일반 타입을 타입 매개변수로 바꾸면 된다. 끝으로 이 과정에서 발생하는 비검사 경고를 해결해준다.
  • class DelayQueue<E extends Delayed> implements BlockingQueue<E>
    • 러한 타입 매개변수를 한정적 타입 매개변수(bounded type parameter) 라고 한다.

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

  • 이왕이면 로타입을 반환하는 메서드말고… 제네릭 메서드로 만들어라..!(반환타입이나 인자가 로타입일 수 있는)
  • 제네릭 싱글톤 팩터리
    • 우선 자원을 생성해 반환하는 팩터리.
    • 싱글톤이니까 해당 자원을 딱 하나만 만듦.
    • 제네릭이라고 한 이유는 그 반환값에 타입 매개변수( type parameter)를 사용하기 때문임.
  • 재귀적 타입 한정
    • recursive type bound

    • 자기 자신이 들어간 표현식을 사용하ㄱ여 타입 매개변수 허용범위 결정.

      // 재귀적 타입 한정을 이용해 상호 비교할 수 있음을 표현
      public static <E extends Comparable<E>> E max(Collection<E> c);
    • 생각해보니 이 말의 뜻은 아 저기 들어갈수 있는 E 타입 매개변수는 그 클래스가 Comparable를 구핸해야되구나! 를 의미할수도 있을듯.

      public interface Comparable<T> {
          /**
           * Compares this object with the specified object for order.  Returns a
           * negative integer, zero, or a positive integer as this object is less
           * than, equal to, or greater than the specified object.
           *
           * <p>The implementor must ensure
           * {@code sgn(x.compareTo(y)) == -sgn(y.compareTo(x))}
           * for all {@code x} and {@code y}.  (This
           * implies that {@code x.compareTo(y)} must throw an exception iff
           * {@code y.compareTo(x)} throws an exception.)
           *
           * <p>The implementor must also ensure that the relation is transitive:
           * {@code (x.compareTo(y) > 0 && y.compareTo(z) > 0)} implies
           * {@code x.compareTo(z) > 0}.
           *
           * <p>Finally, the implementor must ensure that {@code x.compareTo(y)==0}
           * implies that {@code sgn(x.compareTo(z)) == sgn(y.compareTo(z))}, for
           * all {@code z}.
           *
           * <p>It is strongly recommended, but <i>not</i> strictly required that
           * {@code (x.compareTo(y)==0) == (x.equals(y))}.  Generally speaking, any
           * class that implements the {@code Comparable} interface and violates
           * this condition should clearly indicate this fact.  The recommended
           * language is "Note: this class has a natural ordering that is
           * inconsistent with equals."
           *
           * <p>In the foregoing description, the notation
           * {@code sgn(}<i>expression</i>{@code )} designates the mathematical
           * <i>signum</i> function, which is defined to return one of {@code -1},
           * {@code 0}, or {@code 1} according to whether the value of
           * <i>expression</i> is negative, zero, or positive, respectively.
           *
           * @param   o the object to be compared.
           * @return  a negative integer, zero, or a positive integer as this object
           *          is less than, equal to, or greater than the specified object.
           *
           * @throws NullPointerException if the specified object is null
           * @throws ClassCastException if the specified object's type prevents it
           *         from being compared to this object.
           */
          public int compareTo(T o);
      }

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

  • 한정적 와일드 카드를 사용하여 여러타입을 받을수 있게 하여 그 유연성을 높여라.
  • 위에서 말한 한정적 타입 매개변수
  • 그 중에선 와일드 카드 사용도 가능함 (”?”)
    • 생산자일때

      // class Integer extends Number ...
      public void pushAll(Iterable<? extends E> src) {
          for (E e : src) {
              push(e);
          }
      }
      ****
    • 소비자 일떄(중요, 내 정보를 인자로 받은 컬렉션에 넣으려면, 내것이 일반적이여야하기 때문에 super를 썼다는 것이 중요)

      // E의 상위 타입의 Collection이어야 한다.
      public void popAll(Collection<? super E> dst) {
          while(!isEmpty()) {
              dst.add(pop());
          }
      }

비한정적 와일드 카드에 대해서(Unbounded wildcards)

List<?>
public class Test {
    public static <E> void main(String[] args) {
        List<?> list2 = new ArrayList<Integer>();
        list1.add(1);  // 컴파일 에러
    }
}

List<?> 이것에는 값을 추가로 넣을수 없다. 이미 참조하고 있는 것은 상관 없

이런 상황에 응용 가능


public static void printList(List<?> list) {
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}

다만, 개발자는 어떤 타입의 객체를 담은 리스트가 들어올지도 모르는 상황에서 특정 타입의 객체를 들어온 리스트에 넣어줄 수는 없다. 그래서 null 값만 넣어줄 수 있는 것 같습니다.

https://hwan33.tistory.com/20

https://stackoverflow.com/questions/29342117/what-is-the-purpose-of-list-if-one-can-only-insert-a-null-value

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

  • 신중하긴 하는데 어떤 부분에서 신중해야할지 기억이 나지 않음.
  • 힙오염 과 관련된 이야기.
import java.util.ArrayList;
import java.util.List;

public class Example {
    static void dangerous(List<String>... stringLists) {
        List<Integer> intList = List.of(42);
        Object[] objects = stringLists;
        objects[0] = intList; // 힙 오염 발생
        String s = stringLists[0].get(0); // ClassCastException
    }

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("Hi there");
        dangerous(stringList);
    }
}

https://inpa.tistory.com/entry/JAVA-☕-제네릭-힙-오염-Heap-Pollution-이란

하지만 제네릭이나 매개변수화 타입의 varargs 매개변수를 받는 메서드가 실무에서 매우 유용하기 때문에, 위의 예제처럼 제네릭 varargs 매개변수를 받는 메서드를 선언할 수 있도록 했다. 대표적으로 아래와 같이 Arrays.list(T... a), EnumSet.of(E first, E... set)과 같은 메서드가 있다.

`// Arrays.Java
@SafeVarargs
@SuppressWarnings("varargs")
public static List asList(T... a) {
return new ArrayList<>(a);
}

// EnumSet.java
@SafeVarargs
public static <E extends Enum> EnumSet of(E first, E... rest) {
EnumSet result = noneOf(first.getDeclaringClass());
result.add(first);
for (E e : rest)
result.add(e);
return result;
}`

아이템 33. 타입 안전 이종 컨테이너를 고려하라

  • 뭐더라? 이름부터 어려움.

`public class Favorites {
// 제네릭을 중첩해서 썼으므로 class 리터럴이면 뭐든 넣을 수 있다.
private Map<Class<?>, Object> favorites = new HashMap<>();

public <T> void putFavorite(Class<T> type, T instance) {
    favorites.put(Objects.requireNonNull(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, "Java");
    f.putFavorite(Class.class, Favorites.class);

    String favoriteString = f.getFavorite(String.class);
    Class<?> favoriteClass = f.getFavorite(Class.class);

    // 출력 결과: Java Favorites
    System.out.printf("%s %s%n", favoriteString, favoriteClass.getName());
}

}`

위 Favorites 클래스는 타입 안전하다. String을 요청했을 때 Integer를 반환하는 등의 예외가 발생하지 않는다. 하지만 이 구현에도 단점은 존재한다. 먼저, 넣을 때 잘못 넣으면 오류가 발생할 수 있습니다.

f.putFavorite((Class)Integer.class, "This is not integer !!!"); Integer notInteger = f.getFavorite(Integer.class); // ClassCastException

이것을 해결하기 위한 방법으로

public <T> void putFavorite(Class<T> type, T instance) {
    favorites.put(Objects.requireNonNull(type), Objects.requireNonNull(type.cast(instance)));
}
  • 이것의 가정
    • type.cast는 실패하면 null을 반환하나?

또한 실체화가 불가능한 타입은 넣을 수 없다. 그러니까, String이나 String[]은 저장할 수 있지만, List<String>은 저장할 수 없다. 우회하기 위한 방법으로는 슈퍼 타입 토큰을 사용할 수 있다. 슈퍼 타입을 토큰으로 사용한다는 뜻이다. 스프링 프레임워크에서는 이를 클래스로 미리 구현해놓았다.

List<String> pets = Arrays.asList("강아지", "고양이"); f.putFavorite(new TypeRef<List<String>>(){}, pets); List<String> list = f.getFavorite(new TypeRef<List<String>>(){});

**requireNonNull 을 사용하는 이유**

  • 빠른 실패 가능과 가독성
    • 앞으로 이거 써보기.

Class 클래스에 대해

  • 자바는 클래스와 메타 정보를 java.lang 패키지에 소속된 클래스로 관리한다.
  • 클래스 이름, 생성자 정보, 필드 정보, 메서드 정보.
  • Class 객체 얻기

질문

  1. 타입 이종 컨테이너는 언제 사용할수 있을까?
profile
SMART https://github.com/dongseoki?tab=repositories

0개의 댓글