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

2022. 10. 11. 22:14· BE/Java
목차
  1. 아이템 28. 배열보다는 리스트를 사용하라
반응형

EFFECTIVE JAVA(이펙티브 자바)

 

 

 

이 포스팅에서 작성하는 내용은 EFFECTIVE JAVA(이펙티브자바) 에서 발췌하였습니다.


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

 

배열 vs 제네릭타입

1. 공변 / 불공변

  • 배열은 공변이다.(함께 변한다)
    즉, Sub가 Super의 하위 타입이라면 배열 Sub[]는 배열 Super[]의 하위 타입이 된다.
  • 제네릭은 불공변이다.
    즉 서로 다른 타입 Type1과 Type2가 있을 때, List<Type1>은 List<Type2>의 하위타입도 상위타입도 아니다.

 

ex 1) 배열과 리스트의 차이 1

// 배열에 넣기
Object[] objectArray = new Long[1];
objectArray[0] = "Long에 문자열 넣기";       // 런타임 시 ArrayStoreException 발생

// 리스트에 넣기
List<Object> objectList = new ArrayList<Long>();
objectList.add("Long에 문자열 넣기");        // 컴파일 단계에서 오류 발생
  • Long으로 선언된 배열과 리스트에 String을 넣을 수 없다.
  • 배열의 경우 런타임 때 오류를 발견하지만, 리스트는 컴파일 시에 바로 알 수 있다.

 

2. 실체화

  • 배열은 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다.
  • 제네릭은 원소타입을 컴파일타임에만 검사하며, 런타임에는 알수조차 없다.
  • 즉, 제네릭은 타입 정보가 런타임에는 소거된다.
    소거는 제네릭이 지원되기 전의 레거시 코드와 제네릭 타입을 함께 사용할 수 있게 해주는 매커니즘으로, 자바 5가 제네릭으로 순조롭게 전환될 수 있도록 함

 

 

위의 주요 차이들로 인해 배열과 제네릭은 잘 어우러질 수 없다.

배열은 제네릭 타입, 매개변수화 타입, 타입 매개변수로 사용할 수 없다.

ex) new List<E>[], new List<String>[], new E[] 식으로 작성 시 컴파일할 때 오류가 발생한다.

 

 

 

제네릭 배열을 막은 이유

  • 타입이 안전하지 않기 때문
  • 이를 허용한다면 컴파일러가 자동 생성한 형변환코드에서 런타임 시, ClassCastException이 발생할 수 있음
  • 런타임에 ClassCastException이 발생하는 일을 막는 제네릭 타입과는 어긋남

 

ex 2) 제네릭 배열 생성을 허용하지 않는 이유

List<String>[] stringLists = new List<String>[1]; // (1) 
List<Integer> intList = List.of(42);              // (2)
Object[] objects = stringLists;                   // (3)
objects[0] = intList;                             // (4)
String s = stringLists[0].get(0);                 // (5)
  • 제네릭 배열을 생성하는 (1) 이 허용된다고 가정하자
  • (2)는 원소가 하나인 List<Integer>을 생성한다.
  • (3)은 (1)에서 생성한 List<String> 배열을 objects에 할당한다.
    배열은 공변(Covariant)이므로 아무 문제없다.
  • (4)는 (2)에서 생성한 List<Integer>의 인스턴스를 Object 배열의 첫 원소로 저장합니다.
    제네릭은 소거 방식으로 구현되어 성공한다.
    즉,런타임에서 타입이 사라지므로 List<Integer>은 List가 되고 List<Integer>[]는 List[]가 되며, ArrayStoreException이 발생하지 않는다.
  • 문제가 발생하는 부분은 (5)이다.
    List<String> 인스턴스만 담겠다고 선언한 stringLists 배열에 List<Integer> 가 담겨있고, 이를 String으로 형변환하기 때문에 런타임 시 ClassCastException 이 발생한다.

 

 

 

배열로 형변환 할 때 오류가 발생하는 경우

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

 

 

ex 3) 제네릭을 적용해야하는 소스

public class Chooser {
    private final Object[] choiceArray;
    
    public Chooser(Collection choices) {
        this.choiceArray = choices.toArray();
    }
    
    public Object choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceArray[rnd.nextInt(choiceArray.length)];
    }
}
  • 이 클래스는 컬렉션 안의 원소 중 하나를 무작위로 선택해 반환하는 choose 메서드가 있다.
  • 이 클래스를 사용하려면 choose 메서드를 호출할 때마다 반환된 Object를 원하는 타입으로 형변환해야한다.
  • 혹시나 다른 타입의 원소가 들어있다면 런타임 시에 형변환을 하며 오류가 발생할 것이다.

 

ex 4) ex 3 소스를 제네릭으로 적용

public class Chooser<T> {
    private final T[] choiceArray;

    public Chooser(Collection<T> choices) {
        this.choiceArray = (T[]) choices.toArray();
    }
	
    public Object choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceArray[rnd.nextInt(choiceArray.length)];
    }
}
  • 위와 같이 제네릭으로 적용 시, 비검사 경고가 발생한다.
  • T가 무슨 타입인 지 알 수 없으니 컴파일러는 이 형변환이 런타임에도 안전한 지 보장할 수 없다는 메시지
  • 제네릭에서는 원소의 타입 정보가 소거되어 런타임에는 무슨 타입인 지 알 수 없다는 것을 기억해야한다.
  • 이 소스는 동작하지만, 컴파일러가 안전을 보장하지 못한다.
    이 경고를 제거하려면, 배열 대신 리스트를 사용하면 된다.

 

ex 5) ex 4 소스를 리스트로 적용

class Chooser<T> {
    private final List<T> choiceList;

    public Chooser(Collection<T> choices) {
        this.choiceList = new ArrayList<>(choices);
    }

    public T choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceList.get(rnd.nextInt(choiceList.size()));
    }
}
  • 코드가 복잡해지고 조금 느려졌겠지만, 런타임 시 ClassCastException을 피할 수 있다.

 

 

정리

  • 배열은 공변이고 실체화되어 런타임 시 타입 안전하지 않지만, 컴파일 시에는 그렇지 않다.
  • 반면, 제네릭은 불공변이고 타입 정보가 소거되어 그 반대다.
  • 위와 같은 특성으로 인해 둘을 섞어쓰기는 쉽지 않으며, 둘을 섞어 사용하다가 오류나 경고를 만나면 배열을 리스트로 대체해보자. 
반응형

'BE > Java' 카테고리의 다른 글

[Effective Java] 아이템 30. 이왕이면 제네릭 메서드로 만들라  (1) 2022.10.13
[Effective Java] 아이템 29. 이왕이면 제네릭 타입으로 만들라  (0) 2022.10.12
[Effective Java] 아이템 27. 비검사 경고를 제거하라  (0) 2022.10.07
[Effective Java] 아이템 26. 로 타입은 사용하지 말라  (0) 2022.10.06
[Effective Java] 아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라  (0) 2022.10.05
  1. 아이템 28. 배열보다는 리스트를 사용하라
'BE/Java' 카테고리의 다른 글
  • [Effective Java] 아이템 30. 이왕이면 제네릭 메서드로 만들라
  • [Effective Java] 아이템 29. 이왕이면 제네릭 타입으로 만들라
  • [Effective Java] 아이템 27. 비검사 경고를 제거하라
  • [Effective Java] 아이템 26. 로 타입은 사용하지 말라
멍목
멍목
개발 관련 새롭게 알게 된 지식이나 좋은 정보들을 메모하는 공간입니다.
반응형
멍목
김멍목의 개발블로그
멍목
전체
오늘
어제
  • 분류 전체보기 (514)
    • BE (190)
      • Spring (21)
      • Java (141)
      • Kotlin (6)
      • JPA (22)
    • FE (33)
      • Javascript (16)
      • Typescript (0)
      • React (5)
      • Vue.js (9)
      • JSP & JSTL (3)
    • DB (32)
      • Oracle (22)
      • MongoDB (10)
    • Algorithm (195)
    • Linux (8)
    • Git (6)
    • etc (42)
    • ---------------------------.. (0)
    • 회계 (4)
      • 전산회계 2급 (4)
    • 잡동사니 (2)

블로그 메뉴

  • 홈
  • 관리

공지사항

인기 글

태그

  • 자바공부
  • 자바 테스팅 프레임워크
  • Effective Java
  • 자기공부
  • 코틀린
  • vue3 공부
  • JPA
  • 자기개발
  • 알고리즘공부
  • Java to Kotlin
  • 자기 개발
  • junit5
  • 더 자바 애플리케이션을 테스트하는 다양한 방법
  • 프로젝트로 배우는 Vue.js 3
  • 이펙티브 자바
  • 자바 공부
  • MongoDB 공부
  • 알고리즘 공부
  • Oracle
  • 코테 공부
  • 자기 공부
  • 더 자바 Java 8
  • 이펙티브자바
  • java 8
  • MongoDB with Node.js
  • JPA 공부
  • 자바 개발자를 위한 코틀린 입문
  • MongoDB 기초부터 실무까지
  • 코테공부
  • 전산회계 2급 준비

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.0
멍목
[Effective Java] 아이템 28. 배열보다는 리스트를 사용하라
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.