Search
Duplicate

아이템 32 - 제네릭과 가변인수를 함께 쓸 때는 신중하라

작성자
챕터
5장 - 제네릭
최종 편집
2023/07/29 04:24
생성 시각
2023/07/29 04:03

가변인수

가변인수는 메서드에 넘기는 인수의 개수를 클라이언트가 조절할 수 있게 해준다
가변인수 메서드를 호출시, 가변인수를 담기 위한 배열이 자동으로 하나 만들어진다

제네릭과 가변인수

실체화 불가 타입은 런타임에는 컴파일타임보다 타입관련 정보를 적게 담는다.
거의 모든 제네릭과 매개변수화 타입(List<?>, List<T>) 은 실체화 되지 않는다.
메서드 선언시 실체화 불가 타입으로 varargs 매개변수를 선언하면 컴파일러가 경고를 보낸다.
warning: [unchecked] Possible heap pollution from parameterized vararg type List<String>
Java
복사
매개변수화 타입의 변수가 타입이 다른 객체를 참조하면 힙 오염이 발생한다.

힙 오염

import java.util.ArrayList; import java.util.List; public class HeapPollutionExample { public static void main(String[] args) { // String 타입을 저장하는 List를 생성 List<String> stringList = new ArrayList<>(); stringList.add("Hello"); stringList.add("World"); // String 타입을 저장하는 List를 다른 객체로 참조하도록 형변환 List<Integer> integerList = (List<Integer>) (Object) stringList; // 정상적인 동작을 기대하지만 컴파일러는 이 부분에서 힙 오염 경고를 발생시킴 for (Integer num : integerList) { System.out.println(num); // 런타임 시 ClassCastException 발생 } } }
Java
복사
List<Integer> integerList = (List<Integer>) (Object) stringList; ⇒힙 오염이 발생하는 부분
public class VarArg { static void dangerous(List<String>... stringLists){ List<Integer> intList = List.of(42); Object[] objects = stringLists; objects[0] = intList; // 힙 오염 발생 //objects[0] == List<Integer> intList 의 참조를 가진다. //System.out.println(objects[0].getClass()); String s = stringLists[0].get(0); // class cast exception System.out.println(s); } public static void main(String[] args) { dangerous(List.of("a", "b","c")); } }
Java
복사
지금 상태는 objects[0] == List<Integer> intList의 참조를 가지는 상태다.
따라서 get(0)을 했을때 나오는건, String이 아닌 List.class

근데 왜 씀?

varargs 매개변수를 받는 메서드가 실무에서 유용하게 쓰인다
Arrays.asList(T… a)
Collections.addAll(Collection<? super T> c, T… elements)
EnumSet.of(E first, E… rest)

@SafeVarargs

Java 7 이후로 어노테이션 사용시 @SupressedWarnings 없이 경고를 제거할 수 있다.
메서드 작성자가, 메서드가 타입 안전함을 보장하는 장치다
따라서 타입 안정성이 확실할때만 사용하자 == 웬만하면 쓸일 없으니까 쓰지말자
public static <T> List<T> pickTwo(T a, T b, T c){ switch (ThreadLocalRandom.current().nextInt(3)){ case 0: return List.of(a,b); case 1: return List.of(a,c); case 2: return List.of(b,c); } throw new AssertionError(); } public static void main(String[] args) { List<String> attributes = pickTwo("아이패드","아이폰","맥북"); for (String attribute : attributes) { System.out.println(attribute); } }
Java
복사
배열 없이 제네릭으로만 사용해서 안전하다.