본문 바로가기

카테고리 없음

Effective java 정복기 5장

728x90

아이템_26 로 타입은 사용하지 말자

 

제네릭 클래스,제네릭 인터페이스

- 클래스, 인터페이스 선언에 타입 매개변수가 쓰일 경우

 

public class Book<T> {
	private T t;
    
    public T get() {
    	return t;
    }
    
    public void set(T t) {
    	this.t = t;
    }
}

로타입(raw type)

- 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않았을 경우

ex) List<E>에서 로타입은 List이다

 

즉, 정리하자면 로 타입을 사용하는 것을 자바 언어 차원에서 막아 놓지는 않았지만 사용하지 않는 것을 권장하며 그 이유는 앞선 코드에서 보았듯이 로 타입 사용시 제네릭이 안겨주는 안정성과 표현력을 모두 잃게 되기 때문으로 정리할 수 있다.

그럼 왜 이런 로 타입이 존재하는가에 대한 의문이 생기게 되는데 바로 호환성 때문이다. 자바 1.4 이하 버전에 작성된 코드와의 마이그레이션 호환성을 지켜주기 위해서 타입 이레이저라는 방식을 사용하여 호환성을 지켜주고 있다.

 

로 타입이 사용되는 곳이 있다면?

첫번째 예외는 class리터럴에는 로 탑을 사용해야한다.

class리터널이란 String.class,Integer.class등을 말하며 이를 하나의 객체로 생각하면된다.

 

String.class의 타입은 Class<String>, Integer.class는 Class<Integer>로 볼 수 있으며 Class>T>가 로타입인

Class의 매개변수화 타입이라고 생각하면된다.

 

 

즉, 로 타입은 제네릭이 도입되기 전 코드와의 호환성을 위해 제공될 뿐

로 타입으 런타임에 예외가 발생할 수 있으니 되도록 사용을 

 

아이템_27 배열보다는 리스트를 사용하라

 

공변과 불공변

- 배열은 공변이다. 공변성이란 자신이 상속받은 부모 객체로 타입을 변화시킬 수 있다라는 것을 의미한다.

sub라는 클래스가 super라는 클래스의 하위클래스라고 할때 배열 Sub[]는 super[]의 하위타입이 된다.

 

** 런타임시점에 에러를 발생시킴

**리스트를 사용하면 애초에 컴파일 시 오류가 발생함.

 

Object[] arr = new String[10]; // 컴파일 OK (공변성 때문에)
arr[0] = 123; // 런타임 오류 (ArrayStoreException 발생)

 

List<String> list = new ArrayList<>();
// list.add(123); // 컴파일 오류 발생! (안전함)

 

  • 배열 대신 List<E>를 사용하면 타입 안정성이 높아짐.
  • 제네릭을 활용할 수 있어서 코드가 더 안전하고 유연해짐.
  • 컴파일 타임에 타입 오류를 잡을 수 있어, 런타임 오류를 줄일 수 있음.

아이템_31 한정적 와일드카드를 사용해 API유연성을 높여라

 

 

pushAll(produer - extends)

public class Stack<E> {
    private List<E> list = new ArrayList<>();
    
    public void pushAll(Iterable<E> src) {
        for(E e : src) {
            push(e);
        }
    }
    
    public void push(E e) {
        list.add(e);
    }
}

 

위의 Stack 클래스의 pushAll() 메서드는 결함이 존재합니다. Iterable의 원소 타입이 스택의 원소 타입과 일치하면 잘 작동하지만 타입이 다를 경우 매개변수화 타입이 불공변이기 때문에 컴파일 에러가 발생하게 됩니다.

public class Stack<E> {
    private List<E> list = new ArrayList<>();

    public void pushAll(Iterable<? extends E> src) {
        for(E e : src) {
            push(e);
        }
    }

    public void push(E e) {
        list.add(e);
    }
}

pushAll() 입력 매개변수 타입은 E의 Iterable이 아니라 E의 하위타입의 Iterable 이라고 변경해주어 타입을 안전하고 깔끔하게 사용할 수 있게 됩니다.

 

popAll (consumer - super)

 

public void popAll(Collection<E> dst) {
    while (!isEmpty()) {
        dst.add(pop());
    }    
}

주어진 컬렉션의 원소 타입이 스택의 원소 타입과 일치한다면 말끔히 컴파일되고 문제없이 동작하게 됩니다.

 

public void popAll(Collection<? super E> dst) {
    dst.addAll(list);
    list.clear();
}

E의 Collection이 아니라 E의 상위 타입의 Collection 이라고 변경해주어 깔끔하게 사용이 가능

 

PECS 공식 (producer-extends, consumer-super)

PECS를 풀어서 설명하면 매개변수화 타입 T가 생상자라면 <? extends T>를 사용하고, 소비자라면 <? super T>를 사용한다는 의미입니다.

 

 

아이템_32 제네릭과 가변인수를 쓸 때는 신중해라

 

        // dangerous호출
        List<String> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        dangerous(list1, list2);

 

    public static void dangerous(List<String> ... stringLists){
        List<Integer> myInts = new ArrayList<>();
        myInts.add(42);
        Object[] arr = stringLists;
        arr[0] = myInts; // 힙 오염 발생
        String s = stringLists[0].get(0); // ClassCastException
    }

- 에러발생 함.

- 가변인수 매개변수는 최대한 지양하여 코드를 작성하자

728x90