[새싹] 이펙티브 자바 Item 26 - 로 타입은 사용하지 말라

채상엽·2022년 6월 24일
0

Sproutt 2nd - Spring Study

목록 보기
31/32
post-thumbnail

Raw 타입은 사용하지 말라

제네릭 클래스 또는 제네릭 인터페이스란? : 클래스와 인터페이스 선언에 타입 매개변수가 쓰인 것

예를 들면 List<E> 와 같은 형태가 제네릭 인터페이스가 되겠다.

여기서 꺽쇠(<>)안에 정의하는 타입이 매개변수화 타입(parameterized type)이라고 한다.

Raw 타입이란?

Raw 타입이란 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말한다. 예를 들면 List<E> 에서 List가 Raw 타입이 되는 것이다. 그렇다면 왜 Raw 타입을 사용하지 말라고 하는 것일까? 다음 예제를 통해 살펴보자

Raw 타입을 지양해야하는 이유

// Stamp 인스턴스만 취급
private final Collection stamps = ...;

이 코드를 사용하면 실수로 Stamp 대신 Coin 따위를 넣어도 아무 오류 없이 컴파일에 성공하고 실행된다.(경고 표시 정도는 컴파일러가 해줄 것이다). 그리고 아마 이 컬렉션에서 요소를 꺼내 올때 Stamp로 받아오려고 한다면 실제 타입은 Coin이기 때문에 ClassCastException이 발생할 것이다.

이럴 경우 아래와 같이 제네릭 타입으로 선언해주면 컴파일 시점에 오류를 잡아낼 수 있다.

private final Collection<Stamp> stamps = ...;

즉, 이제부터는 stamps 객체에는 Stamp의 인스턴스만 넣어야 함을 컴파일러가 인지하게 된 것이다.

이렇듯 Raw 타입을 쓰는 것을 언어 차원에서 막아 놓지는 않았지만 절대로 사용해서는 안된다. 왜냐하면 Raw 타입을 사용하면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 되기 때문이다.

List와 같이 Raw 타입으로 사용해서는 안되지만, List<Object>와 같이 임의 객체를 허용하는 매개변수 타입을 정의하는 것은 괜찮다. 그렇다면 이 둘의 차이는 무엇일까?

ListList<Object> 의 차이

  • 컴파일러에게 제네릭 타입 사용 여부를 선언 하였는지의 여부의 차이가 있다

  • List에는 List<String>을 넘길 수 있지만, List<Object>에는 넘길 수 없다

    • StringObject의 하위 타입이 아니기 때문이다.
  • List와 같이 사용하면 타입 안전성을 잃게 된다.

그렇다면 타입을 지정하고 싶지 않은 경우에는 어떻게 하면 좋을까?

만약 비한정적으로 타입을 선언하고 싶은 경우에는 와일드카드 타입(unbounded wildcard type)을 사용하는 것이 좋다. 아래와 같이 사용해볼 수 있다

static int numElementsInCommon(Set<?> s1, Set<?> s2) {...}

그렇다면 와일드카드가 주는 장점은 무엇일까? 간단하게 말하자면 와일드카드 타입은 안전하고, Raw 타입은 안전하지 않다. Collection<?>에는 null 이외에 다른 원소를 넣을 수 없다. 만약 다른 원소를 넣으려 한다면 컴파일 시점에 오류를 발생시킨다.

정리

  • Raw 타입을 사용하면 런타임에 에러를 발생시킬 수 있으므로 지양하는게 좋다
  • 가능한 컴파일 타임에 에러를 발생시키는 것이 안전하다.
  • 즉, Set<Object>Set<?>는 안전하지만 Set은 안전하지 않다.
profile
프로게이머 연습생 출신 주니어 서버 개발자 채상엽입니다.

0개의 댓글