언어/Effective Java

이펙티브 자바 Item 26,27,28 (제네릭 관련)

now0204 2024. 6. 4. 11:30

 

Item 26 (로 타입은 사용하지 마라)

 

로타입은 List list = new ArrayList(); 형태로 사용하는 방식이다.

매개변수화 타입은 List<Object> list = new ArrayList<>(); 방식

 

로타입은 뭐든지 다 들어간다.

  List list = new ArrayList();
                 list.add("문자열");
                 list.add(1);

                 String text = (String) list.get(0);
                 Integer number = (Integer) list.get(1);

 

문자열, 숫자 가리지 않고 다 넣을 수 있다. 하지만, 타입 캐스팅해줘야하는 경우가 빈번해진다. 

제네릭을 통해 타입 안정석과 표현력을 가지고 가자 

 

로타입이 쓰고 싶을 땐?

 

로타입을 사용하지 않는다면, 메서드를 만들때 타입에 맞는 메서드를 여러개 구현해야하는 불편함이 있다. 

private int count(Set<Object> destination, Set<String> source) {
          ....
          return result;
      }

      private int count(Set<Object> destination, Set<Integer> source) {
          ....
          return result;
       }

 

이럴 경우 비한정적 와일드카드 타입을 사용해서 해결한다.

  • 와일드 카드는 어떤 타입이든 상관없이 받을 수 있지만, 타입의 안정성을 보장해준다.
   private int count(Set<?> destination, Set<?> source) {
         ....
         return result;
     }

 

로타입을 써도 좋은 곳 

 

1. 클래스 리터럴에는 로타입을 해도 괜찮다. (클래스 리터럴은 매개변수화(List) 타입을 사용하지 못하기 때문이다.)

    - 자바 명세에서 class리터럴에 매개변수화 타입을 사용하지 못하게 됨 (List.class, String[].class 등)

 

2. instanceof에 사용해도 괜찮다. (런타임에는 제네릭 타입이 지워진다. instanceof에서 리스트타입인지 알고 싶다면, 로타입이든 와일드 카드든 둘 중 하나를 사용하자)

     - instanceof 연산자는 비한정적 와일드 카드 외 매개변수화 타입 적용할 수 없음

if (o instanceof Set) { // 로 타입
	Set<?> s = (Set<?>) o; // 와일드카드 타입
}

 


Item 27 (비검사 경고를 제거하라)

 

1.제거할 수 있는 경고는 제거하자

2. 제거할 수 없는 경고는 숨기자

3. SuppressWarnings("uncheck") 어노테이션은 경고를 감춰준다. (함수선언, 변수선언 부분)

    

*SUppressWarning은 절대 클래스 전체에 적용하지마라, 메서드나 생성자에 달려있으면 지역 변수나 아주 짧은 메서드 혹은 생성자로 옮겨두자 


Item 28 배열보다는 리스트를 사용하라

 

제네릭은 런타임에 안전하다 !

  1. 배열은 공변으로 존재하고, 제네릭은 불공변으로 존재한다! 
  2. 다른 말로 배열은 실체화 가능 타입이고, 제네릭은 실체화 불가 타입이다.  (E, List<E>와 같은 타입) -> List<?>, Map<?,?>은 실체화 될 수 있는 타입이다. 

실체화 가능 타입은 런타임에도 타입정보를 가지고 있다.

제네릭은 런타임에 타입정보를 가지고 있지 않다. -> new List<E>[], new E[]같은 제네릭을 배열로 만드는 행위 불가능하다. 

 

*실체화 불가 타입은 런타임에는 컴파일타임보다 타입 정보를 적게 가지는 타입(공변이 잘 안됨) 

List<String>[] stringLists = new List<String>[1];  //1
List<Integer> intList = List.of(42);               //2
Object[] objects = stringLists;                    //3
objects[0] = intList;                              //4
String s = stringLists[0].get(0);                  //5

 

위와 같은 상황을 가정한다면, 5번에서 에러가 발생한다. 

 

이런 상황은 배열을 제네릭으로 만들 수 없어 귀찮은 상황이다. 

 

제네릭 컬렉션은 자기 자신의 배열을 반환할 수 없다(예외가 있긴함)

 

가변인수 메서드를 제네릭과 함께 쓸때는 알 수 업는 경고를 발생시킨다 (@SafeVarags 어노테이션으로 해결가능하다)

 

@SafeVarargs
    public static void print(List... names) {
        for (List<String> name : names) {
            System.out.println(name);
        }
    }
  • 가변인수 메서드는 호출 시 가변인수 매개변수로 담은 배열이 만들어지는데, 원소가 실체화 불가 타입이라면 경고가 발생함!! (List...names) -> ...이 뭔지알고! 넣어! 

 

배열 대신 컬렉션을 사용하자 

 

배열로 형변환시 발생하는 배열 생성 오류는 대부분 List<E>로 해결가능하다. 

 

public class Chooser {
    private final Object[] choiceArray;

    public Chooser(final Object[] choiceArray) {
        this.choiceArray = choiceArray;
    }

    public Object choose(){
        Random random = ThreadLocalRandom.current();
        return choiceArray[random.nextInt(choiceArray.length)];
    }
}
  • 아무타입의 배열도 다 받을 수 있게 해두었는데, 타입 에러 날 가능성이 크다 (형변환 오류)
public class ListChooser {
    private final List<T> choiceList;

    public ListChooser(final Collection<T> choices) {
        this.choiceList = new ArrayList<>(choices);
    }
    
    public T choose(){
        Random random = ThreadLocalRandom.current();
        return choiceList[random.nextInt(choiceList.size())];
    }
}
  • 대입받는 List의 <> 구현에 따라서 변화하므로, 형변환 오류가 발생하지 않는다.

 


참고자료 

 

https://velog.io/@alkwen0996/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EC%95%84%EC%9D%B4%ED%85%9C28-%EB%B0%B0%EC%97%B4%EB%B3%B4%EB%8B%A4%EB%8A%94-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC

 

[이펙티브 자바] 아이템28 | 배열보다는 리스트를 사용하라

배열과 리스트 차이점 첫 번째 - 배열은 공변인 반면 리스트는 불공변이다. > 공변: 함께 변한다 불공변: 함께 변하지 않는다. 배열의 경우 가 의 하위 타입이라면 는 배열 의 하위 타입이 된다.

velog.io

 

https://javabom.tistory.com/43

 

이펙트브자바 제네릭 (item 26,27,28)

제네릭 아이템26,27,28 Item 26 로타입은 사용하지 마라 로타입은 왜 사용하면 안될까? 로타입은 List list = new ArrayList(); 형태로 사용하는 방식이다. 이번장에서 왜 로타입을 사용하면 안되는지, 그렇

javabom.tistory.com

https://velog.io/@alkwen0996/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EC%95%84%EC%9D%B4%ED%85%9C27-%EB%B9%84%EA%B2%80%EC%82%AC-%EA%B2%BD%EA%B3%A0%EB%A5%BC-%EC%A0%9C%EA%B1%B0%ED%95%98%EB%9D%BC

 

[이펙티브 자바] 아이템27 | 비검사 경고를 제거하라

할 수 있는 한 모든 비검사 경고를 제거하라. [비검사 경고가 발생하는 코드] 위 코드는 비검사 경고가 발생한다. 컴파일러의 안내에 따라 코드를 수정하면 비검사 경고가 사라진다. 하지만, 컴

velog.io

https://velog.io/@alkwen0996/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EC%95%84%EC%9D%B4%ED%85%9C26-%EB%A1%9C-%ED%83%80%EC%9E%85%EC%9D%80-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EB%A7%90%EB%9D%BC

 

[이펙티브 자바] 아이템26 | 로 타입은 사용하지 말라

제네릭 클래스와 제네릭 인터페이스 > 클래스와 인터페이스 선언에 타입 매개변수가 쓰이면, 이를 제네릭 클래스 혹은 제네릭 인터페이스라고 한다. 인터페이스는 원소의 타입을 나타내는 타입

velog.io