brunch

You can make anything
by writing

C.S.Lewis

by 이승현 Apr 17. 2018

제네릭(Generics)

#23 새로 작성하는 코드에서는 원천(raw) 타입을 사용하지 말자

Effective Java - 제네릭(Generics)


Generics을 이용하면 어떤 타입의 객체를 허용할지 컴파일러에게 알려줘서 캐스트 코드를 컴파일러가 자동으로 만들어 줍니다.

그리고 잘못된 타입의 객체는 컴파일 시에 미리 알 수 있습니다.

따라서 런타임 시 발생할 수 있는 에러를 미리 대처할 수 있는 장점이 있습니다.


#01 Generics


제네릭의 장점을 극대화시키고 복잡성을 최소화하는 방법에 대해 알아보겠습니다.





#23 새로 작성하는 코드에서는 원천(raw) 타입을 사용하지 말자


제목에서 말하는 원천(raw) 타입이란, 실 타입 매개 변수(ex String, Integer...)가 없이 사용되는 제네릭 타입을 말합니다.


아래와 같이 List<E>에 대한 원천 타입은 List입니다.

원천 타입을 이용하면 ClassCastException과 같은 런타임 에러가 발생할 수 있습니다.

#02 raw type


이를 원천 타입이 아닌 매개변수화(parameterized) 타입으로 선언되면, 컴파일 시에 자동으로 잘못된 타입으로 변환을 미리 체크해 줄 수 있습니다.

#03 parameterized type




List? List<Object>?


어떤 타입이든 이용할 수 있다는 점에서 원천 타입 List와 List<Object>는 같아 보이지만, 차이가 있습니다.


List : 제네릭 타입의 검사가 생략

List<Object> : 어떤 타입의 객체도 저장할 수 있다는 것을 컴파일러에 명시적으로 알림


아래 코드는 원천 타입을 이용하고 있습니다.

제네릭 타입의 검사가 생략되므로 컴파일은 되지만, ClassCastException 에러가 발생합니다.

#04 raw type


unsafeAdd 메서드의 인사를 List에서 List<Object>로 바꾸면 아래와 같습니다.

이때는 컴파일 에러가 발생합니다.

java: incompatible types: java.util.List<java.lang.String> cannot to converted to java.util.List<java.lang.Object>
#05 List<Object>


말 그대로 List<String>을 List<Object>로 변환할 수 없다는 내용입니다.

String은 Object의 서브 클래스이기 때문에 형 변환이 가능할 거 같지만, List<String>는 List<Object>의 서브 타입이 아닙니다.

오히려 List<String>은 원천 타입인 List의 서브 타입입니다.


List나 List<Object> 둘 다 문제가 발생할 수 있습니다.




언바운드 와일드카드 타입(unbounded wildcard type)


컬렉션의 요소 타입이 미지정 또는 어떤 타입이든 상관없다면 언바운드 와일드카드 타입(unbounded wildcard type)을 이용할 수 있습니다.


예를 들어, 두 개의 Set 모두에 들어있는 요소의 개수를 반환하는 메서드를 원천 타입을 이용해서 구현할 수 있습니다.

문제없이 잘 동작하지만 원천 타입이기 때문에 수정을 하다가 문제가 발생할 수 도 있습니다.

#06 raw type Set


실 타입 매개변수를 모르거나, 어떤 타입이든 상관없다면 물음표(?)를 이용할 수 있습니다.

언바운드 와일드카드 타입이라 부르는데, 이는 어떤 타입의 객체도 이용할 수 있다는 의미입니다.

#07 unbounded wildcard type


원천 타입보다 언바운드 와일드카드 타입은 안정성 측면에서 더 나은 점이 있습니다.

하지만 아래와 같이 Collection<?>에는 어떤 타입의 요소 건(not null) 마음대로 추가할 수 없습니다.

컬렉션 타입 불변성을 깨지 못하게 컴파일러가 에러로 처리하는 건데, 결국 read만 할 수 있게 됩니다.

#08 compile error


이를 해결하기 위해 제네릭 메서드(generic method)나 바운드 와일드카드 타입(bounded wildcard type)을 이용할 수 있는데 나중에 다루겠습니다.




결론은 원천 타입을 이용하면 런타임 에러가 발생할 수 있으므로, 이용해서는 안됩니다.

(원천 타입은 제네릭이 나오기 전에 생긴 기존 코드와 호환성을 위해 존재함.)


Set : 원천 타입, 제네릭 타입 시스템 이용 X, 안정성 보장 X

Set<Object> : 매개 변수화 타입, 어떤 타입의 객체도 저장할 수 있음. 안정성 보장 O

Set<?> : 언바운드 와일드카드 타입, 미 지정 or 어떤 타입의 객체도 저장할 수 있음. 안정성 보장 O





매거진의 이전글 클래스와 인터페이스
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari