본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

[Effective Java] 새로 작성하는 코드에서는 원천(raw) 타입을 사용하지 말자.

by 돼지왕왕돼지 2016. 11. 21.
반응형

 [Effective Java] 새로 작성하는 코드에서는 원천(raw) 타입을 사용하지 말자.


1.5, ?, ? extends, ? super, ? 한정, actual type parameter, bound wildcard, class literal, Effective JAVA, extends, formal type parameter, instanceof, int.class, list, List.class, List, List.class, List, List, List.class, parameterized type, raw type, readonly, Set, Set, string, String[].class, Super, type paramter, unbounded wildcard, [Effective Java] 새로 작성하는 코드에서는 원천(raw) 타입을 사용하지 말자., 기본형 타입, 런타임, 매개변수, 매개변수화 타입, 바운드 와일드 카드, 배열 타입, 불가능, 삭제, 상호 호환성, 새 코드, 새로 작성하는 코드, 서브 타입, 서브 타입 규칙, 실 타입 매개변수, 언바운드 와일드 카드, 언바운드 와일드 카드 타입, 예외, 운용성, 원천 타입, 제네릭 인터페이스, 제네릭 클래스, 제네릭 타입, 제네릭 타입 정보, 제네릭의 장점, 추가 불가, 캐스팅, 컬렉션 요소, 컴파일 에러, 클래스 리터럴, 타입 매개변수, 타입 안전, 표현력, 형식 타입 매개변수


-
하나 이상의 타입 매개변수(type parameter)를 선언하고 있는 클래스나 인터페이스를 제네릭 클래스 또는 제네릭 인터페이스라고 한다.
제네릭 클래스와 인터페이스를 합해서 제네릭 타입이라고 부른다.
각 제네릭 타입에서는 매개변수화 타입(parameterized type)들을 정의한다. ( <> 이용 )
실 타입 매개변수(actual type parameter)들은 제네릭 타입의 형식 타입 매개변수(formal type parameter)와 각각 대응된다.
( List<String> 에서 String 이 실 타입 매개변수이고, 기존 T 혹은 E 가 타입 매개변수이다. )


-
각 제네릭 타입에서는 원천(raw)타입을 정의하는데, 원천 타입은 실 타입 매개변수가 없이 사용되는 제네릭 타입의 이름을 말한다.
( List<String> 에서 원천타입은 List )


-
제네릭의 장점
    컴파일 시점에 에러를 검출하기 좋다.
    컬렉션에서 요소를 가져올 때, 삭제할 때 캐스팅 할 필요가 없다.


-
타입 매개변수를 주지 않고 원천타입으로 컬렉션이나 다른 제네릭 타입을 여전히 사용할 수 있다. ( 제네릭은 1.5 이후 등장 )
하지만 그렇게 해서는 안 된다.
원천 타입을 사용하면 제네릭의 장점인 타입 안전과 표현력 모두를 포기하는 것이다.


-
제네릭에는 서브 타입 규칙이 있다.
List<String> 은 원천 타입인 List 의 서브 타입이지만 매개변수 타입인 List<Object> 의 서브 타입은 아니다.
List 와 같은 원천 타입을 사용하면 타입의 안전성을 상실하게 되지만, List<Object> 와 같은 매개변수 타입을 사용하면 그렇지 않다.


-
제네릭 서브 타입을 보고 오히려 유연성이 떨어지는 것이 아니냐고 물을 수 있겠지만 그렇지 않다.
언바운드 와일드 카드 타입(unbounded wildcard type) 바운드 와일드 카드 타입(bound wildcard type) 이 있기 때문이다.


-
언바운드 와일드 카드 타입은 제네릭 타입을 사용하고 싶으나, 실 타입 매개변수를 모르거나, 어떤 타입이든 관계없다면
타입 대신 ? 를 사용하는 것이다.
List<?> 는 모든 종류의 List 를 받는다.
기존 원천 타입과 무슨 차이냐고?

? 로 표시된 녀석에는 null 이 아닌 어떤 타입도 마음대로 추가할 수 없다.
즉 reaonly 처럼 작동한다.



-
바운드 와일드 카드 타입은 ? extends, ? super 등의 조건을 주어 ? 를 한정지을 수 있다.


-
런타임 시에는 제네릭 타입의 정보가 없어진다.


-
새 코드에는 원천 타입을 사용하지 않는다는 규칙에 두 가지 예외가 있다.
이유는 런타임 시에는 제네릭 타입 정보가 없어진다는 것 때문에 발생한다.


    1. 원천 타입은 클래스 리터럴(class literal) 형태로 사용해야 한다.
        원천 타입을 매개변수화 타입과 사용할 수 없다.(배열 타입과 기본형 타입은 가능)
        즉, List.class. String[].class, int.class 는 적합하지만, List<String>.class, List<?>.class 는 불가능하다.

    2. 언바운드 와일드 카드 타입이 아닌 매개변수화 타입에 대해 instanceof 연산자를 사용할 수 없다.
        제네릭 타입과 함께 instanceof 연산자를 사용할 때는 다음과 같이 하는 것이 좋다.

if ( o instanceof Set ){ // 원천 타입

Set<?> m = (Set<?>) o; // 언바운드 와일드 카드 타입

...

}




Summary


원천 타입을 사용하면 런타임 시 예외가 생길 수 있으므로 앞으로 새 코드에는 사용하지 말자.
원천 타입을 아직 사용할 수 있는 이유는 기존 코드와의 상호 호환성, 운용성 때문일 때 만이다.
Set<Object>는 매개변수화 타입으로써 어떤 객체도 포함할 수 있는 Set 이고, Set<?>는 언바운드 와일드 타입이다.
Set 은 원천 타입으로 타입 안전이 보장되지 않는다.

!!! 에러는 가능한 빨리, 최고는 컴파일 시점에 발견하는 것이 가장 좋다.





반응형

댓글0