2023-java-study / book-study

북 스터디 기록 레포지토리
0 stars 0 forks source link

[Item 32] List.of() 가 안전한 이유 #111

Closed gmelon closed 1 year ago

gmelon commented 1 year ago

List.of()는 생성한 List의 참조를 바깥으로 반환합니다. 책에서는 참조를 바깥으로 반환하는 건 안전하지 않다고 하는데

List.of()의 경우 이게 안전할 수 있는 이유는

  1. 해당 메서드가 반환하는 List가 불변이기 때문일까요?
  2. 아니면 단순히 제네릭을 사용하기 때문에 컴파일 타임에 타입 안전성이 보장될 수 있기 때문일까요??
NuhGnod commented 1 year ago

책에서 참조를 바깥으로 반환하는 건 안전하지 않다 고 말한 부분이 몇 페이지인지 알수 있을까요??

gmelon commented 1 year ago

@NuhGnod 헉 제가 페이지를 안 적었군요 List.of() 관련 내용은 p.195 하단부터 p.196 상단부분 이고

참조를 바깥으로 반환하는 건 안전하지 않다 이 부분은 p.193 입니다.

NuhGnod commented 1 year ago

p.193에서 말하는 배열의 참조는 매개변수 배열의 참조를 의미한다고 생각합니다. 그래서 코드 32-2는 실제로 파라미터가 T...args인데 이로 인해 생성된 매개변수 배열 return args로 밖으로 참조를 내보내고 있어, 클라이언트에서 접근가능합니다.

음 저는 우선 List.of 메서드가 참조를 밖으로 노출하지 않는다고 생각했습니다. 그 이유는 List.of()메소드가 다중정의되어있긴하지만, 반한되는 값들은 같 list를 반환해서 몇개만 보면 됩니다.

ListN 생성자 메서드

@SafeVarargs
        ListN(E... input) {
            // copy and check manually to avoid TOCTOU
            @SuppressWarnings("unchecked")
            E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
            for (int i = 0; i < input.length; i++) {
                tmp[i] = Objects.requireNonNull(input[i]);
            }
            elements = tmp;
        }

List12 생성자 메서드

List12(E e0, E e1) {
            this.e0 = Objects.requireNonNull(e0);
            this.e1 = Objects.requireNonNull(e1);
        }

빈 리스트

    static <E> List<E> of() {
        return ImmutableCollections.emptyList();
    }
static final List<?> EMPTY_LIST = new ListN<>();

static <E> List<E> emptyList() {
        return (List<E>) ListN.EMPTY_LIST;
    }

인수가 1~2개

static <E> List<E> of(E e1, E e2) {
        return new ImmutableCollections.List12<>(e1, e2);
    }

인수가 3개 이상

static <E> List<E> of(E e1, E e2, E e3) {
        return new ImmutableCollections.ListN<>(e1, e2, e3);
    }

인수갯수에 맞춰, 인스턴스 생성자의 파라미터로 넘겨주는 형태임.

varargs사용

@SafeVarargs
    @SuppressWarnings("varargs")
    static <E> List<E> of(E... elements) {
        switch (elements.length) { // implicit null check of elements
            case 0:
                return ImmutableCollections.emptyList();
            case 1:
                return new ImmutableCollections.List12<>(elements[0]);
            case 2:
                return new ImmutableCollections.List12<>(elements[0], elements[1]);
            default:
                return new ImmutableCollections.ListN<>(elements);
        }
    }

결국 위 ListN생성자를 보면 varargs매개변수를 사용했고, 안전하다는게 보장되었으니(p.195의 2조건 만족) @SafeVarargs 어노테이션을 사용했습니다. 그리고, 참조를 바깥으로 내보냈냐를 보면, E...input의 input 배열의 참조를 내보내야하는데, 우선 내부에서 사용되는 elements배열은 new Object[]로 새롭게 생성된 배열이 input배열의 참조에 관여되지 않았습니다. 그래서 저는 input배열은 varargs의 목적대로만 쓰였기 때문에(p.193상단) 메서드가 안전하고 또한, 참조도 밖으로 노출되지 않았다고 생각합니다.


그래서 저는 List.of가 안전한 이유는 item32와 관련지어 본다면, 참조를 밖으로 내보내지 않음에 있다고 생각합니다.

ssstopeun commented 1 year ago

그럼 List는 input하는 배열의 참조가 아닌 내용을 새로 배열을 생성해 저장하고 이 배열의 참조를 return하는 것으로 이해했는데 맞을까요?

NuhGnod commented 1 year ago

네 저는 그렇게 생각했습니다