Effective Java의 Item 28 "배열보다는 리스트를 사용하라"의 내용입니다.
Item 28 배열보다는 리스트를 사용하라
의 핵심 개념은 배열과 제네릭은 근본적으로 다른 타입 규칙을 가지고 있기 때문에 둘을 섞어 사용하면 오류가 발생할 수 있다는 것입니다.
배열(Array)
은 같은 타입의 여러 데이터를 하나의 변수에 저장할 수 있는 구조를 말합니다. 예를 들어, int형 배열은 여러 개의 int 값을 한꺼번에 저장할 수 있습니다.
제네릭(Generic)
은 클래스나 메서드에서 사용할 내부 데이터 타입을 컴파일 시점에 미리 지정하는 방법을 말합니다. 예를 들어, List<String>
은 String 타입의 데이터만 담을 수 있는 리스트를 만듭니다.
공변(Covariant)은 한 타입이 다른 타입의 하위 타입(subtype)으로 간주 될 수 있을 때, 이 두 타입을 '공변'이라고 합니다. 예를 들어, 모든 문자열(String)은 객체(Object)이므로, 문자열은 객체의 하위 타입이라고 할 수 있습니다.
공변이라는 단어에서부터 이해가 잘 안됩니다. 그래서 예시를 보면서 이해해보도록 합시다.
예를 들어, 상위 클래스(Super)가 있고 이를 상속하는 하위 클래스(Sub)가 있습니다.
Super sub = new Sub();
배열에서의 공변은 아래처럼 배열에서도 함께 변해서 적용된다는 뜻입니다.
Super[] sub = new Sub[]{};
Ex)
Object[] objectArray = new String[1];
objectArray[0] = "hi"; // 이것은 잘 동작합니다.
String은 Object의 서브타입이므로 String배열은 Object배열로도 변할 수 있습니다.
Ex)
Object[] objects = new Long[1];
objects[0] = "나는 String이라서 들어가면 안돼요";
Long 클래스는 Object 클래스를 상속받습니다. 위 코드는 컴파일 시점에선 에러가 나지 않지만 코드 실행 후 런타임 시점에서 에러가 발생합니다.
불공변
이라는 용어는 타입 시스템에서 사용되는 개념으로, 한 타입이 다른 타입의 서브타입(subtype)이 아니라는 것을 의미합니다.
List<Super> subs = new ArrayList<Sub>(); // 같은 타입이 아니므로 컴파일 에러!
위 코드처럼 Sub가 Super를 상속하더라도 같은 타입이 아니면 컴파일 에러가 뜹니다.
Ex)
List<Object> list = new ArrayList<Long>();
list.add("test");
위에 말한 것처럼 Long이 Object를 상속하더라도 같은 타입이 아니기때문에 컴파일 에러가 뜨게 됩니다.
따라서, 리스트를 사용하면 컴파일 시점에 오류를 잡아낼 수 있고, 이는 런타임에 발생할 수 있는 오류를 사전에 예방할 수 있습니다.
그러나, 배열과 제네릭을 섞어 사용하면 오류가 발생할 수 있습니다. 예를 들어, 제네릭 타입의 배열을 생성하려고 하면 컴파일 오류가 발생합니다.
List<String>[] list = new List<String>[1];
위 코드는 컴파일 오류가 발생합니다.
List<String>[] stringLists = new List<String>[1]; // 컴파일 에러
List<Integer> intList = Arrays.asList(42);
Object[] objects = stringLists; // 배열은 공변이므로 이 작업은 허용됩니다.
위의 코드에서 stringLists는 List<String>
의 배열이므로, 이 배열의 각 원소는 문자열 리스트가 되어야 합니다.
그러나 배열의 공변성과 제네릭의 타입 소거 때문에, 우리는 List<Integer>
를 stringLists[0]에 할당할 수 있게 됩니다.
이는 배열이 런타임에 자신의 원소를 확인하는 반면, 제네릭은 타입 정보가 런타임에 지워지기 때문에(타입 소거)배열과 제네릭이 함께 사용될 때 타입 안정성을 보장할 수 없기 때문입니다.
- 타입 안전성: 리스트는 컴파일 시점에 타입 검사를 수행하기 때문에, 실행 중에 발생할 수 있는 타입 오류를 사전에 방지합니다.
- 유연성: 리스트는 크기 변경이 자유롭고, 풍부한 API를 제공합니다.
기능: 리스트는 다양한 기능(검색, 정렬 등)을 제공합니다.