제네릭 1장

  1. raw type을 사용하지 말라!
  2. 비검사 경고를 제거하라
  3. 배열보다는 리스트를 사용하라

1. raw type을 사용하지 말라!

raw type 이란?
클래스와 인터페이스 선언에 타입 매개변수(ex. )가 있으면 각각 제네릭 클래스, 제네릭 인터페이스라고 부른다. 이것을 제네릭 타입이라고 한다. (ex. List) 제네릭 타입을 정의하면 raw type도 정의되는데, 여기서 List의 raw type이란 List 이다. 즉, 타입 매개변수를 전혀 쓰지 않은 경우 를 말한다.

raw타입을 쓰면 제네릭이 안겨주는 안정성과 표현력을 모두 잃게 된다. 그럼 왜 존재하는가? 자바가 제네릭을 받아들이기 까지 10년이 걸렸다……. 그전에 짜놓은 레거시 코드들이 제네릭 없이 짠 코드가 너무너무너무나도 많기에 “호환성” 을 위해………….

핵심정리

raw 타입을 사용하면 런타임에 예외가 일어날 수 있으니 사용하면 안된다. 제네릭이 도입되기 이전 코드와의 호환성을 위해 제공될뿐!

1
2
3
4
5
Set<Object> 는 어떤 타입의 객체도 저장할수 있는 매개변수화 티입이고,

Set<?> 는 모종의 타입 객체만 저장할수 있는 와일드카드 타입이다.

위의 Set<Object>, Set<?>는 안전하지만, raw 타입인 Set은 안전하지 않다.

2. 비검사 경고를 제거하라

할수 있는 한 모든 비검사 경고를 제거하라! 모두 제거하게 된다면 그 코드는 타입 안정성이 보장된다.
만약 타입 안정성이 확실한데 컴파일러의 경고를 없애고 싶다면 @SuppressWarnings(“unchecked”)를 사용하자.

3. 배열보다는 리스트를 사용하라

뱌열과 제네릭 타입에는 중요한 차이 2가지가 있다.

  1. 배열은 공변이다. Sub가 Super의 하위타입이라면 배열 Sub[]은 배열 Super[]의 하위 타입이 된다.(공변이란? 즉 함께 변한다는 뜻이다.)

  2. 제네릭은 불공변이다. 즉 서로 다른 타입 타입A, 타입B가 있을 때 List<타입A>는 List<타입B>의 하위 타입도 아니고 상위 타입도 아니다.

런타입 에러

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class Main {

public static void main(String[] args) {


Object[] objectArray = new Long[1];

objectArray[0] = "타입이 다를걸?";
}
}

아래의 에러 발생
Exception in thread "main" java.lang.ArrayStoreException: java.lang.String
at gege.Main.main(Main.java:10)

컴파일 에러(컴파일이 되지 않음)

1
2
3
4
5
6
7
8
9

public class Main {

public static void main(String[] args) {

List<Object> ol = new ArrayList<String>();

}
}

왜 배열보단 리스트?! 리스트를 써야 할가? 개발자들이 개발코드를 쭉 이어나갈때 예측하지 못한 에러는 런타임시 알게 된다면 개발자에게는 해당 부분을 수정하는데 또한 시간을 오래 부어야한다. 요즘 개발을.. vi를 열어 편집하는 일은 거의 없다고 생각한다. ide에서 자체적으로 컴파일 안될꺼라 알려준다!

제네릭 배열을 만들지 못한 이유를 생각해보자

  1. 너무나도 당연하게 타입 safe하지 않기 때문이다. 컴파일러가 자동 생성한 형변환 코드에서 런타입에 ClassCastException이 발생 할수 있다. 런타임에 해당 에러 발생을 막아주겠다는 제네릭 타입 시슨템의 취지에 어긋난다.

결론

  1. 배열은 공변, 실체화되는 반면
  2. 제네릭은 불공변이고 타입 정보가 소거된다.
  3. 배열은 런타입에는 타입 안전하지만 컴파일타임에는 그렇지 않다. 제네릭은 그반대이다.

이펙티브자바 3판 5장 제네릭 참고! (잊지 않기 위해 블로그에 올립니다)