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

> Effective Java 3e 아이템 28를 요약한 내용 입니다.

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

## 배열은 공변이다.

`Sub`가 `Super`의 하위 타입이라면 배열 `Sub[]`는 배열 `Super[]`의 `하위 타입`이 된다. 반면 제네릭은 `불공변`이다. **이것만 보면 제네릭에 문제가 있다고 생각할 수도 있지만 사실 문제가 있는 건 배열 쪽이다.**

```java
Object[] objectArray = new Long[1];
objectArray[0] = "타입이 달라 넣을 수 없다."; // ArrayStoreException을 던진다.
```

다음 코드도 살펴 보자

```java
List<Object> ol = new ArrayList<Long>(); // 호환되지 않는 타입이다.
ol.add("타입이 달라 넣을 수 없다.");
```

어느 쪽이든 `Long`용 저장소에 String을 넣을 수는 없다. 다만 배열 에서는 그 실수를 `런타임`에야 알게 되지만, 리스트를 사용하면 컴파일할 때 바로 알 수 있다.

## 배열은 실체화가 된다.

배열은 `런타임`에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다. 그래서 이전 예제에서 보듯이 `Long` 배열에 `String`을 넣으려 하면 `ArrayStoreException`이 발생한다. 반면, 앞서 이야기했듯 `제네릭`은 타입 정보가 `런타임`에는 소거된다.

이상의 주요 차이로 인해 `배열`과 `제네릭`은 잘 어우러지지 못한다. 배열은 `제네릭 타입`, `매개변수화 타입`, `타입 매개변수`로 사용할 수 없다. 즉, 코드를 `new List<E>[]`, `new List<String>[]`, `new E[]` 식으로 작성하면 컴파일할 때 제네릭 배열 생성 오류를 일으킨다.

### 제네릭 배열을 만들지 못하게 막은 이유는 무엇일까?

**타입 안전하지 않기 때문이다.** 이를 허용한다면 컴파일러가 자동 생성한 형변환 코드에서 런타임에 `ClassCastException`이 발생할 수 있다.

`E`, `List<E>`, `List<String>` 같은 타입을 `실체화 불가 타입`이라 한다. 실체화 되지 않아서 런타임에는 컴파일 타임보다 타입 정보를 적게 가지는 타입이다. `소거 메커니즘` 때문에 매개 변수화 타입 가운데 실체화 될 수 있는 타입은 `List<?>`와 `Map<?,?>`같은 `비 한정적 와일드 카드` 타입뿐이다.

### 배열을 제네릭으로 만들 수 없어 귀찮을 때도 있다.

`**제네릭 컬렉션`에서는 자신의 원소 타입을 담은 배열을 반환하는 게 보통은 불가능하다.\*\* 또한 제네릭 타입과 `가변 인수 메서드`를 함께 쓰면 해석하기 어려운 경고 메시지를 받게 된다. 이 문제는 `@SafeVarargs` 에너테이션으로 대처할 수 있다.

> @SafeVarargs는 구체적으로 어떻게 사용할 수 있을까?\
> &#x20;제네릭을 가변 매개변수로 사용한 경우 잠재적 에러에 대한 경고를 무시할 수 있다.\
> &#x20;@SuppressWarning를 이용하여 제네릭 매개변수에 대한 경고 무시를 대체하기 위해 나옴

```java
public class JavaExample{  
    @SafeVarargs
    private void print(List... names) {  
        for (List<String> name : names) {  
            System.out.println(name);  
        }  
    }  
    public static void main(String[] args) {  
        JavaExample obj = new JavaExample();  
        List<String> list = new ArrayList<String>();  
        list.add("Kevin");  
        list.add("Rick"); 
        list.add("Negan");
        obj.print(list);  
    }      
}
```

배열로 형변환 할 때 제네릭 배열 생성 오류나 `비검사 형변환` 경고가 뜨는 경우 대부분은 배열인 `E[]` 대신 컬렉션인 `List<E>`를 사용하면 해결된다. 코드가 조금 `복잡`해지고 `성능`이 살짝 나빠질 수도 있지만, 그 대신 타입 안전성과 `상호 운용성`은 좋아진다.

```java
public class Chooser {
    private final Object[] choiceArray;

    public Chooser(Collection<T> choices) {
    choiceArray = choises.toArray();
    }

    public Object choose() {
    Random rnd = ThreadLocalRandom.current();
    return choiceArray[rnd.nectint(choiceArray.length)];
    }
}
```

위 클래스는 매번 형 변환해야 하니 리팩토링이 필요하다.

```java
public class Chooser<T> {
    private final List<T> choiceList;

    public Chooser(Collection choices) {
    choiceList = new ArrayList<>(choises);
    }

    public Object choose() {
    Random rnd = ThreadLocalRandom.current();
    return choiceList.get(rnd.nextint(choiceList.size()));
    }
}
```

이전 예제보다 코드양이 조금 늘었고 아마도 조금 더 느릴 테지만, 런타임에 `ClassCastException`을 만날 일은 없으니 그만한 가치가 있다.

## 정리

**제네릭은 `불공변`이고 타입 정보가 소거된다.** 그 결과 배열은 런타임에는 타입 안전하지만 컴파일 타임에는 그렇지 않다. 제네릭은 반대다. 그래서 둘을 섞어 쓰기란 쉽지 않다. 둘을 섞어 쓰다가 `컴파일 오류`나 경고를 만나면 가장 먼저 배열을 리스트로 `대체`하는 방법을 적용해보자.

## 출처

* <https://blog.itthis.me/115>

  \-


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://incheol-jung.gitbook.io/docs/study/effective-java/undefined-3/2020-03-20-effective-28item.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
