티스토리 뷰
제네릭 타입을 새로 만드는걸 배워보자.
예시로 아이템 7에서 다룬 스택코드를 가져왔다.
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_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;
}
// isEmpty(), ensureCapacity()
}
이 클래스를 제네릭으로 바꾼다고 해도 현재 버전을 사용하는 클라이언트에는 아무런 해가 없다.
오히려 지금 상태에서의 클라이언트는 스택에서 꺼낸 객체를 형변환 해야하는데, 이때 런타임 오류가 날 위험이 있다.
제네릭 타입 만들기
- 클래스 선언에 타입 매개변수 추가하기 (컴파일되지 않음)
public class Stack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new E[DEFAULT_INITIAL_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;
}
// isEmpty(), ensureCapacity()
}
- E와 같은 실체화 불가 타입으로는 배열을 만들수 없기에 오류가 발생함
해결책1. 제네릭 배열 생성을 금지하는 제약을 대놓고 우회하는 방법
- Object[] 을 생성한 다음 제네릭 배열로 형변환 해보자.
- 이렇게하면 오류 대신 경고를 보낼 것이다.
- 가능은 하지만 일반적으로 타입 안전하지 않다.
// 해결책 1
@SuppressWarnings("unchecked") // 안전하다는 결론을 내렸을 때만
public Stack() {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; // 경고
}
- 컴파일러는 타입 안전한지 증명할 방법이 없지만 우리는 할 수 있다.
- 따라서 이 비검사 형변환이 타입 안전성을 해치지 않음을 우리 스스로 확인해야 한다.
- elements는 private 필드에 저장되고, 클라이언트로 반환되거나 다른 메서드에 전달되는 일이 전혀 없다.
- push() 를 통해 배열에 저장되는 원소의 타입은 항상 E 이다.
- 따라서 이 비검사 형변환은 안전한다는 결론을 내릴 수 있다.
해결책2. elements 필드의 타입을 E[] → Object[]로 바꾸는 법
public E pop() {
if (size == 0) {
throw new EmptyStackException();
}
@SuppressWarnings("unchecked") // 증명 후 경고 최소한으로 숨기기
E result = (E) elements[--size]; // 형변환시 경고
elements[size] = null;
return result;
}
- E는 실체화 불가 타입이므로 컴파일러는 런타임에 이뤄지는 형변환이 안전한지 증명할 방법이 없다.
- 마찬가지로 스스로 증명하고 경고를 숨길 수도 있다.
1번 방법 vs 2번 방법
1번
- 가독성이 더 좋다.
- 형변환을 배열 생성 시 단 한 번만 해주면 된다.
- 현업에서 더 선호한다.
- 배열의 런타임 타입이 컴파일 타임과 달라 힙 오염 (heap pollution Item32)을 일으킨다.
2번
- 배열에서 원소를 읽을 때마다 형변환을 해줘야 한다.
결론
- 클라이언트에서 직접 형변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다.
- 그러니 새로운 타입을 설계할 때는 형변환 없이도 사용할 수 있도록 하라.
- 기존 클라이언트에는 아무 영향을 주지 않으면서, 새로운 사용자를 훨씬 편하게 해주는 길이다.
반응형
'책 > 이펙티브자바' 카테고리의 다른 글
아이템 59. 라이브러리를 익히고 사용하라 (0) | 2021.09.15 |
---|---|
아이템 57. 지역변수의 범위를 최소화하라 (0) | 2021.09.15 |
아이템28. 배열보다는 리스트를 사용하라 (0) | 2021.09.01 |
아이템41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라 (0) | 2021.08.31 |
아이템40. @Override 애너테이션을 일관되게 사용하라 (0) | 2021.08.31 |
링크
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- 드림코딩
- Spring Security
- HTTP 완벽가이드
- 이펙티브자바 아이템60
- java
- js api
- JPA 연관관계 매핑
- 이펙티브자바 아이템59
- JS 딥다이브
- 이펙티브자바 스터디
- 가상 면접 사례로 배우는 대규모 시스템 설계 기초
- HTTP 완벽 가이드
- js promise
- 모던자바스크립트
- 프로그래머스 SQL
- 백준
- REST API
- 패스트캠퍼스 컴퓨터공학 완주반
- 이펙티브자바
- dreamcoding
- http
- BOJ
- 김영한 JPA
- js array
- 집 구하기
- 프로그래머스
- GCP
- 김영한 http
- 킹수빈닷컴
- 백기선 스터디
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
글 보관함