-
이펙티브 자바 Item 26,27,28 (제네릭 관련)언어/Effective Java 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 배열보다는 리스트를 사용하라
제네릭은 런타임에 안전하다 !
- 배열은 공변으로 존재하고, 제네릭은 불공변으로 존재한다!
- 다른 말로 배열은 실체화 가능 타입이고, 제네릭은 실체화 불가 타입이다. (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://javabom.tistory.com/43
'언어 > Effective Java' 카테고리의 다른 글
이펙티브 자바 Item 30 - 이왕이면 제네릭 메서드로 만들라 (0) 2024.06.04 이펙티브 자바 Item29 - 이왕이면 제네릭 타입을 사용하라 (0) 2024.06.04 이펙티브 자바 Item 25 - 톱레벨 클래스는 한파일에 하나만 담자 (0) 2024.06.04 이펙티브 자바 Item 24 - 멤버 클래스는 되도록 static으로 만들라 (1) 2024.06.04 이펙티브 자바 - Item 21 - 인터페이스는 구현하는 쪽을 고려해서 설계하라 (0) 2024.06.04