prgrms-web-devcourse / BE-Team-preArmand-Book-study

2 stars 2 forks source link

[아이템 28] 제네릭에서 배열 반환 불가문제 #17

Closed Leeyerimearth closed 2 years ago

Leeyerimearth commented 2 years ago

166p.의 3번째 문단의 내용이 이해가 가지 않습니다. 혹시 설명해주실 분 계신가요?

예컨대 제네릭 컬렉션에서는 자신의 원소 타입을 담은 배열을 반환하는 게 보통은 불가능하다.( 또 아이템 33에서 그 방법을 설명)

이 뜻이, 컬렉션에 이미 구현되어있는 메서드 중에서 컬렉션을 배열로 변환해주는 메서드가 없다는 뜻 인가요? 아이템 33에서는 가능한 방법을 설명해준다는데.. 봐..봐도 모르겠습니다 🥲 아이템 33에 배열로 반환하는게 있던가요

epicblues commented 2 years ago

☠️☠️☠️☠️☠️뇌피셜입니다.

저는 아이템 33의 교훈(?)을 클래스 리터럴을 적극적으로 활용하라는 것으로 봤습니다. 실제로 컬렉션을 배열로 변환해주는 메서드는 있으나, @SuppressWarnings을 사용하고 있더라구요. 예를 들어 java.util.ArrayList를 보면 5번째 줄에서 비검사 형변환 경고가 뜹니다.

    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

toArray의 매개변수로 클래스 리터럴을 사용해보는 메서드를 만들어봤습니다. 리터럴을 사용하니 형변환 경고가 뜨지 않았습니다. 제가 제대로 한 건지는 모르겠습니다...🤣


class WrappedArrayList<T> {

  private final Object[] elementData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  private final int size = 10;

  public <A> A[] toArray(Class<A[]> type) {
   // 비검사 형변환 경고가 뜨지 않습니다.
    return Arrays.copyOf(elementData, size, type);
  }

}

  public static void main(String[] args) {
    var b = new WrappedArrayList<Integer>();
    Integer[] c = b.toArray(Integer[].class);
}
kimziou77 commented 2 years ago

☠️☠️☠️☠️☠️

민성님이 하신 말씀에 덧붙여서 이야기 해보겠습니다 호호하하

제네릭 컬렉션에서는 자신의 원소 타입을 담은 배열을 반환하는 게 보통은 불가능하다.

이제 보통이 아닌 케이스들을 하나씩 보게 될텐데

먼저 29장 이왕이면 제네릭 타입으로 만들라 - 171p 예시를 보면

new E[INITIAL_CAPACITY]; 와 같은 실체화 불가 타입으로 배열을 만들 수 없다.

그래서 29장에서는 다음의 예시에서 이를 우회하는 방법을 두가지 말해줍니다. image 1. E[] elements = (E[]) new Object[INITIAL_CAPACITY] 와 같이 변경해줍니다. -- 이 경우 E[]는 object[]의 하위타입이므로 바뀌지만 object[]안에 어떤것이든 들어갈 수 있으니 런타임 안정성이 떨어집니다 2. object[] element 로 변경하고, (E) elements[size] 와 같이 값을 꺼낼때마다 E로 형변환 해줍니다. -- 이경우 element에 넣어주는 것이 E라는 것을 개발자는 알고있지만 컴파일러는 알아볼수 없습니다.

심지어 이런 경우에는 컴파일러가 보장할수없으니 저희가 @SuppressWarnings("unchecked") 와 같은 문구로 내가 봤는데 안전할거야(?) 라고 약속을 또 해줘야 합니다.

여기서 이렇게 제네릭 배열(?) 을 우회해서 사용하는것이 귀찮고 덜 안전하다는 사실을 알 수 있습니다.


이제 33장을 보면 새롭게 우회할 수 있는 방법을 알려줍니다. 3. 클래스리터럴 (String.class, Integer.class) 을 이용한 방법인데, 이는 위에서 말한 E[]같은걸 반환은 하지만 타입 안전하지 않은 상황을 해결해줍니다. image 여기서 마지막 type.cast(favirotes.get(type)) 이부분에서 컴파일 에러를 발생시킬 수 있습니다. image 타입토큰을 사용하면 Integer[]와 String[]를 잘 반환할 수 있습니다..

결과적으로는 이렇게 본인에 타입매개변수를 직접 사용하는것이 아니라 키를(여기서는 Class) -> 타입 매개변수로 사용하면 타입 안전 이종 컨테이너 (컬렉션)라고 하고, 이를 사용하면 맨 위에서 시작한 질문인

자신의 원소 타입을 담은 배열을 타입 안전하게 반환할 수 있다는 것 같습니다..


-> 컬렉션에 이미 구현되어있는 메서드 중에서 컬렉션을 배열로 변환해주는 메서드가 없다는 뜻 인가요?

컬렉션이 데이터를 담는 컨테이너라고 생각한다면 제네릭한 배열은 생성할 수 없기 때문에(new E[INITIAL_CAPACITY];) 제네릭한 배열을 (안전하게) 반환해주는 방법 또한 없다- 라는 의미인 것 같습니다!