본문 바로가기
개발 공부/Java

이펙티브 자바 아이템 28 - 배열보다는 리스트를 사용하라 - 완벽 공략

by 개발인생 2022. 12. 12.
반응형

아이템 28 - 배열보다는 리스트를 사용하라 - 완벽 공략

이 글은 백기선 님의 이펙티브 자바 강의와 이펙티브 자바 3 / E 편을 참고하여 작성하였습니다.

@SafeVarags

Varags 는 가변인자이다.

매서드 매개변수로 불특정 다수를 받을 수 있는 인자이다.

여기에 제네릭을 썻을 때 복잡한 문제가 생기게 된다.

제네릭과 배열 은 잘 어울리지 않지만 같이 사용할 수 있는 유일한 경우의 수가 가변 인자 이다.

class Main {
   public static void main(String[] args) {
      List<String>[] stringLists = new ArrayList<String>[1]; // 컴파일 에러

   }
}

위의 코드처럼 제네릭 타입의 배열을 만들 수 없다.

public class SafeVaragsExample {

//    @SafeVarargs // Not actually safe!
    static void notSafe(List<String>... stringLists) {
        Object[] array = stringLists; // List<String>... => List[], 그리고 배열은 공변이니까.
        List<Integer> tmpList = List.of(42);
        array[0] = tmpList; // Semantically invalid, but compiles without warnings
        String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
    }


    public static void main(String[] args) {
        SafeVaragsExample.notSafe(List.of("a", "b", "c"));
    }
}

static void notSafe(List<String>... stringLists) 메서드와 같이 가변인자를 List 로 만들 수 있다.

가변인자 (...) 는 결국 배열이다.

그래서 static void notSafe(List<String>[] stringLists) 로 선언이 가능하다.

문제는 전달받은 객체가 오염 될 수 있다.

오염 된다는 것은 해당 객체안에 들어가면 안되는 데이터가 들어갔다는 것이다.

때문에 Possible heap pollution from parameterized vararg type 이라는 경고 메세지가 발생하고

해당 경고가 발생하는 이유는 내부적으로 제네릭 타입의 배열이 생기기 때문이다.

그 뒤 발생하는 문제는 아이템28 핵심정리 에서 살펴본 내용과 같다.

public class SafeVaragsExample {

   @SafeVarargs
   static <T> void safe(T... values) {
      for (T value: values) {
         System.out.println(value);
      }
   }

   public static void main(String[] args) {
      SafeVaragsExample.safe("a", "b", "c");
   }
}

반면에 위의 메서드를 보면 제네릭 타입으로 가변인자를 만들었기 때문에

즉, 제네릭 배열이 생기기 때문에 Possible heap pollution from parameterized vararg type 이라는 경고 메세지가 발생하지만

해당 메서드 안에서 하는 일이 안전하다.

    static void notSafe(List<String>... stringLists) {
        Object[] array = stringLists; // List<String>... => List[], 그리고 배열은 공변이니까.
        List<Integer> tmpList = List.of(42);
        array[0] = tmpList; // Semantically invalid, but compiles without warnings
        String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
    }

notSafe 메서드처럼 가변인자를 제네릭으로 받아서 그 값을 그대로 리턴 한다거나 어딘가에 할당 하면 안된다.

굉장히 위험한 코드가 된다.

그래서 무턱대고 @SafeVarargs 를 사용하면 안된다.

경고를 무시한다해서 해당 문제가 없어지는게 아니기 때문이다.

public class SafeVaragsExample {

   @SafeVarargs
   static <T> void safe(T... values) {
      for (T value: values) {
         System.out.println(value);
      }
   }

   public static void main(String[] args) {
      SafeVaragsExample.safe("a", "b", "c");
   }
}

safe 메서드는 안전한 코드이기 때문에 @SafeVarargs 를 붙여 해당 경고를 무시할 수 있다.

그러나 가급적이면 @SafeVarargs 를 사용하지 않도록 노력해야한다.

굳이 @SafeVarargs 를 사용해야하는 경우에는 가변인자를 제네릭으로 받아서

새로운 변수에 할당하지 않고, 리턴하지도 않는 경우에만 사용하는 것을 권장한다.

반응형

댓글