TIL - 20.12.26
[Effective Java] 5장 제네릭 Item 32. 제네릭과 가변인수를 함께 쓸 때는 신중해라
- 제네릭 등 실체화 불가 타입의 배열은 타입 안전성이 깨질 수 있기 때문에 직접 만들 수 없도록 막았다.
- “타입 안전성이 깨진다.” ⇒ “런타임에 ClassCastExcption이 발생할 수 있다.”
- 에러가 발생할 수 있으므로 컴파일 시에 경고로 알려준다.
- 가변인수 메서드를 호출하면 내부적으로 가변인수를 담기 위한 배열이 하나 만들어진다.
- 실체화 불가 타입을 가변 인수로 받는 경우(
String...
이나T...
) 실체화 불가 타입에 대한 배열이 만들어진다는 의미
- 실체화 불가 타입을 가변 인수로 받는 경우(
-
재네릭 배열을 직접 만드는 것은 막으면서, 가변인수에서 생성하는 것은 경고로만 알리는 이유는?
-
T...
와 같은 가변 인자가 실무에서 유용하기 때문이다. -
대표적으로 printf() 등 자바 라이브러리도 이런 메서드를 제공한다.
public PrintStream printf(String format, Object ... args) { return format(format, args); }
-
- 규칙 : 제네릭이나 매개변수화 타입의 varargs 매개변수를 받는 메서드를 정의할 때는 @SafeVarargs를 붙여준다.
- 안전하지 않아도 붙이라는 뜻이 아니라 varargs 메서드를 만들거면 무조건 안전하게 만들라는 뜻
- “안전한 제너릭 varargs 메서드”란?? 다음 두 조건을 만족하는 것을 의미
- varargs 매개변수 배열에 아무것도 저장하지 않는다.
- 그 배열(혹은 복제본)을 외부에 노출하지 않는다. (메서드 내에서만 사용하고 리턴or전달 X )
제너릭 배열 사용시 주의 사항 - 안전하지 않은 제너릭 varargs 메서드
배열과 제네릭의 중요한 차이점은 Type을 확인하는 시점이다.
- 배열 : 런타임에 type 정보를 저장하고 확인
- 제네릭 : 컴파일 타임에 유형 오류를 확인하고 런타임에는 유형 정보가 없다. ⇒ Type Erasure
제너릭 배열을 직접 생성하면 컴파일 오류가 발생한다.
T[] elements = new T[size];
why? 타압 안정성이 깨지기 때문에 막음
다음 코드를 살펴보자
public <T> T[] getArray(int size) {
T[] genericArray = new T[size]; // suppose this is allowed
return genericArray;
}
바인딩되지 않은 제네릭 유형 T 가 Object로 해석 되므로 런타임시 메서드는 다음과 같다.
(T[]와 같은 제너릭 배열을 구현했다면, T라는 타입은 실행시간에선 정확한 자료형으로 지정이 안되니, Object[]로 변환한다.)
public Object[] getArray(int size) {
Object[] genericArray = new Object[size];
return genericArray;
}
그런 다음 메서드를 호출하고 결과를 String 배열 에 저장하면 :
String[] myArray = getArray(5);
코드는 잘 컴파일되지만 런타임에 ClassCastException 과 함께 실패한다.
이는 String [] 참조에 Object [] 를 할당했기 때문이다. 특히 컴파일러에 의한 암시 적 캐스트는 Object [] 를 필수 유형 String [] 로 변환하지 못한다.
이렇듯 제네릭 배열을 직접 초기화 할 수는 없지만, 정확한 유형의 정보가 호출 코드에서 제공되는 경우 동일한 작업을 수행 할 수 있습니다. (⇒ “가변인수 메서드를 호출하면 내부적으로 가변인수를 담기 위한 배열이 하나 만들어져서 사용된다.”) 이 때 발생할 수 있는 ClassCastException을 주의할 것!!
안전하게 작성되지 않은 코드는 컴파일이 아니라 런타임시에 발견될 수 있으므로 이런 위험을 피해야 한다.
REF
2d Array sort
1번 인덱스로 정렬
Arrays.sort(arr, Comparator.comparingInt(a -> a[1]));
좌표 정렬
(x, y) 좌표가 주어졌을 때, y를 오름차순으로 정렬하면서, y가 같다면 x를 오름차순으로 정렬
자바의 Arrays.sort()가 stable sort1로 정렬해주기 때문에, x를 기준으로 우선 정렬 후, y를 정렬하도록 하면 된다.
Arrays.sort(arr, Comparator.comparingInt(a -> a[0]));
Arrays.sort(arr, Comparator.comparingInt(a -> a[1]));
결과
1 6
3 5
0 6
5 7
3 8
5 9
6 10
8 11
8 13
2 13
12 14
=====> sort
(3, 5)
(0, 6)
(1, 6)
(5, 7)
(3, 8)
(5, 9)
(6, 10)
(8, 11)
(2, 13)
(8, 13)
(12, 14)
-
우선 순위가 같은 원소들끼리는 원래의 순서를 따라가도록 하는 정렬 ↩