제네릭을 도입하기 전, 프로그래머는 컬렉션에서 객체를 읽어낼 때마다 형변환을 해야 했다. 하지만 제네릭을 사용하면 컬렉션에 넣는 객체의 자료형이 무엇인지 컴파일러에게 알릴 수 있으므로, 형변환 코드는 컴파일러가 알아서 넣어준다. 잘못된 자료형의 객체를 컬렉션에 넣으려는 시도도 컴파일단계에서 발견하게 된다.


선언부에 형인자(<String>)가 포함된 클래스나 인터페이스는 제네릭 클래스나 인터페이스라고 부른다. 그러나 제네릭 자료형을 형인자 없이 사용할 수도 있다. 이러한 무인자 자료형을 쓰면 형 안전성이 사라지고(아무 자료형을 다 넣을 수 있음) , 제네릭의 장점중 하나인 표현력 측면에서 손해를 보게된다.


 무인자 자료형을 사용하면 안되지만 List<Object>와 같은 자료형은 써도 좋다. List 와 List<Object>의 차이점은 무엇일까? List는 형 검사 절차를 완전히 생략한 것이고, List<Object>는 아무 객체나 넣을 수 있다는 것을 컴파일러에게 알리는 것이다. 코드로 살펴보자.

 
 위의 코드는 컴파일 단계에서 오류는 발생하지 않으나, 실행시켜보면 ClassCastingException이 발생할 것이다. 왜 일까? 이유는 strings에는 String객체만 담을 수 있도록 되어있는데 unsafeAdd메서드에는 무인자 자료형인 List로 인자를 받는다. 인자로 받은 리스트에 Integer객체를 담았다. 그러나 String s = strings.get(0) 을 실행할 때 컴파일러는 String으로 자료형을 변환하려 할 것인데, 실제 인자로 저장되어있는 값은 Integer이기 때문에 에러가 발생한다. 
 이러한 문제점때문에 무인자 자료형을 사용하면 안된다. 그래서 비한정적 와일드카드 자료형이라는 좀 더 안전한 대안을 제공한다. 제네릭 자료형을 쓰고 싶으나 실제 형 인자가 무엇인지 모르거나 신경 쓰고 싶지 않을 때는 형 인자로 "?"를 사용하면 된다.

 이러한 비한정적 와일드카드 자료형에는 null이외의 어떠한 원소도 넣을 수 없으므로, 불변식을 지켜준다. 


 새로 만드는 코드에는 무인자 자료형을 쓰면 안된다고 하였지만, 예외 2가지가 있다. 첫 번째는 클래스 리터럴에는 반드시 무인자 자료형을 사용해야 한다. 자바 표준에 의하면 클래스 리터럴에는 형인자 자료형을 쓸수 없다.(배열 자료형이나 기본 자료형은 쓸수 있다.) 

 두 번째 예외는 instanceof 연산자 사용 규칙에 관한 것이다. 제네릭 자료형 정보는 프로그램이 실행될 때는 지워지기 때문에 instanceof 연산자는 비한정적 와일드카드 자료형 이외의 형인자 자료형에 적용할 수 없다. 그래서 다음과 같이 하는 것이 좋다.

위와 같이 o가 Set객체라는 것이 확실해진 다음에는 와일드카드 자료형 Set<?>로 형변환 해야하는 것에 주의하자.

요약

무인자 자료형을 쓰면 프로그램 실행 도중에 얘외가 발생할 수 있으므로 새로 만드는 코드에는 무인자 자료형을 쓰지 말아야 한다. Set<Object>는 어떤 자료형의 객체도 담을 수 있는 집합의 형인자 자료형이며, Set<?>는 모종의 자료형 객체만을 담을 수 있는 집합을 표현하는 와일드카드 자료형이다 ( add가 안되는? 모종의 자료형 객체가 무슨말이야..). Set은 무인자 자료형으로, 제네릭 자료형 시스템의 일부가 아니다. Set<Object>와 Set<?>은 안전하지만, Set은 안전하지 못하다.



+ Recent posts