728x90
26. Raw Type은 사용하지 말라
- 클래스와 인터페이스 선언에 타입 매개변수가 쓰이면 제네릭 클래스/인터페이스라고 한다.
- 제네릭 클래스와 인터페이스를 합쳐 제네릭 타입이라고 한다.
- 각 제네릭 타입은 매개변수화 타입을 정의하며, <> 안에 실제 타입 매개변수를 나열한다.
- 제네릭 타입을 하나 정의하면 Raw 타입, 타입 매개변수를 전혀 사용하지 타입이 정의된다.
Raw Type 사용으로 인해, 제네릭이 가져오는 안전성과 표현력을 모두 상실한다.
- List<Object>와 List는 엄연히 다르다.
- List<Object>는 컴파일러에 모든 타입 허용을 명확히 전달한 것
- List는 제네릭에서 완전히 벗어난 것
- List<Integer>는 Raw Type의 하위 타입이지만, 타입을 명시한 List<Object>의 하위는 아님
- 원소의 타입을 몰라도 되는 경우가 필요하다면 List<?>처럼 비한정적 와일드카드 타입을 사용하자.
Raw Type이 필요한 경우
- class 리터럴
- instanceof
27. 비검사 경고를 제거하라
가능한 모든 비검사 경고를 제거해야 한다.
- 컴파일 단계에서 경고(Warning)을 보기 위해 다음 옵션을 추가한다.
- -Xlint:uncheck
- 경고를 모두 제거하면, 타입 안전성을 확보할 수 있어 런타임에 ClassCastException 문제에서 자유로워진다.
- 경고 제거가 불가능하지만, 타입 안전성이 확실하다면 @SuppressWarnings을 추가한다. 단, 어노테이션은 최대한 좁은 범위에 적용해야 하며, 무시한 근거에 대한 주석 작성 필요
@SuppressWarnings("unchecked")
public Stack() {
this.elements = (E[]) new Object[DEFAULT_CAPACITY];
}
28. 배열보다는 리스트를 사용하라
배열과 제네릭타입의 차이
- 배열은 공변이고, 제네릭은 불공변이다.
- 배열은 실체화되어, 런타임에도 원소의 타입을 확인하지만, 제네릭 타입은 런타임에는 해당 정보가 없다.
List<E>, List<Integer>와 List<?>의 차이
- List<E>, List<Intger>는 실체화 불가 타입으로, 런타임에는 컴파일 타임보다 타입 정보가 적다
- List<?>는 실체화가 가능하다.
- 배열을 비한정적 와일드카드 타입으로 생성은 가능해도, 유용하지 않다.
배열과 제네릭을 섞어 쓰기 어렵다.
둘을 섞어 사용하면서 생성 오류 및 비검사 형변환 경고가 뜨는 경우, E[]를 List<E>로 바꾸는 것만으로도 쉽게 해결이 가능하며, 약간의 성능 손실로, 타입 안전성과 상호운용성을 챙길 수 있다.
29. 이왕이면 제네릭 타입으로 만들라
일반 클래스를 제네릭으로 만들기 위해 클래스 선언에 타입 매개변수를 추가하라.
public class Stack {
private Object[] elements;
private int size;
public static final int DEFAULT_CAPACITY = 16;
public Stack() {
this.elements = new Object[DEFAULT_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size ==0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
}
Object 배열 생성 후 제네릭 배열로 형변환 제네릭 배열 생성을 금지하는 제약을 우회하기
- 컴파일러 오류 대신 경고로 바뀌지만, 이런 경우 일반적으로 타입 안전성이 떨어진다.
- 타입 안전성을 증명한 후, @SuppressWarnings로 경고를 숨긴다,
- 배열 타입의 선언을 통해 E 타입만 받음을 명시하여 가독성이 더 좋고, 코드도 짧다.
public class Stack<E> {
private E[] elements;
private int size;
public static final int DEFAULT_CAPACITY = 16;
@SuppressWarnings("unchecked")
public Stack() {
this.elements = (E[]) new Object[DEFAULT_CAPACITY];
}
public void push(E e) {
ensureCapacity();
elements[size++] = e;
}
public E pop() {
if (size == 0) {
throw new EmptyStackException();
}
E result = elements[--size];
elements[size] = null;
return result;
}
}
필드의 타입을 실체화 불가 타입의 배열(E[])에서 Object 배열로 변경
- 배열이 반환한 원소를 E로 형변환한다.
- 컴파일러가 E에 대해 런타임에 형변환에 대한 안전성을 확인할 수 없다.
- 타입 안전성을 검증한 후, @SuppressWarning로 경고를 숨긴다.
- 배열에서 원소를 사용할 때마다 형변환이 필요하다.
- 배열의 런타임 타입이 컴파일타임 타입의 다름으로 인해 힙 오염이 발생하는 것을 방지하고 자하는 경우, 이 방법을 사용한다.
public class Stack<E> {
private Object[] elements;
private int size;
public static final int DEFAULT_CAPACITY = 16;
public Stack() {
this.elements = new Object[DEFAULT_CAPACITY];
}
public void push(E e) {
ensureCapacity();
elements[size++] = e;
}
public E pop() {
if (size == 0) {
throw new EmptyStackException();
}
@SuppressWarnings("unchecked")
E result = (E) elements[--size];
elements[size] = null;
return result;
}
}
728x90
'책, 세미나, 컨퍼런스 후기 > 2022' 카테고리의 다른 글
Effective Java 3/E - 6장 열거 타입과 애너테이션 - 1 (0) | 2022.02.27 |
---|---|
Effective Java 3/E - 5장 제네릭 - 2 (0) | 2022.02.13 |
Effective Java 3/E - 4장 클래스와 인터페이스 - 3 (0) | 2022.02.01 |
Effective Java 3/E - 4장 클래스와 인터페이스 - 2 (0) | 2022.01.23 |
Effective Java 3/E - 4장 클래스와 인터페이스 - 1 (0) | 2022.01.16 |