맞는데 왜 틀릴까..?

Java/Effective Java

[Effective Java] Item 29. 이왕이면 제네릭 타입으로 만들라

안도일 2023. 5. 9. 21:14

제공하는 제네릭 타입과 메서드를 사용하는 일은 쉽지만 새로 제네릭 타입을 만드는 일은 조금 어렵다.

제네릭 타입으로 만들기 알맞은 Stack 클래스를 만들어 보자.

 

Object 기반 Stack 클래스 

 

Object를 사용해 구현한 Stack 클래스

스택에서 꺼낸 객체를 형변환 해야 하는데 이때 런타임 오류가 날 위험이 있으므로 제네릭 타입으로 만들자.

 

 

 

배열을 사용한 코드를 제네릭으로 만드는 방법

 

1. 클래스 선언에 타입 매개 변수를 추가 (<E>)

 

 

2. 코드에 쓰인 Object를 적절한 타입 매개변수로 변경

 

 

하지만 Item28에서 설명한 것처럼 E와 같은 실체화 불가 타입으로는 배열을 만들 수 없다.

 

 

해결책

 

1. Object 배열을 생성한 다음 제네릭 배열로 형변환

 

 

오류대신 비검사 형변환 경고가 나타난다. 

 

이 비검사 형변환은 안전하므로 Item27처럼 @SuppressWarnings 애너테이션으로 해당 경고를 숨기자.

 

 

해당 방식은 가독성이 좋다. (배열의 타입을 E []로 선언하여 오직 E 타입 인스턴스만 받음)

 

 

2. elements 필드의 타입을 E []에서 Object []로 변경

 

 

오류가 발생하는 곳에서 형변환 시켜주자.

E는 실체화 불가 타입이므로 컴파일러는 런타임에 이뤄지는 형변환이 안전한지 증명할 방법이 없기 때문에 경고가 뜰 텐데 이번에도 애너테이션을 이용해 경고를 숨기면 된다.

 

 

 힙 오염을 걱정한다면 해당 방식을 사용하자. (힙 오염은 다음에 알아보자)

 

 

한정적 타입 매개변수

 

한정적 매개변수 : 타입 매개변수에 제약을 두는 제네릭 타입

 

java.util.concurrent.DelayQueue는 다음과 같이 선언되어 있다.

 

class DelayQueue<E extends Delayed> implements BlockingQueue<E>

 

  • <E  extends Delayed>는 java.util.concurrent.DelayQueue의 하위 타입만 받는다는 의미다.
  • DelayQueue의 원소에서 형변환 없이 바로 Delayed 클래스의 메서드를 호출할 수 있다.

 


 

클라이언트에서 직접 형변환 해야하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하므로 새로운 타입을 설계할 때 제네릭으로 만들자.