이펙티브자바 정리 4

이봐요이상해씨·2021년 12월 28일
0

목록 보기
4/6

26. raw 타입은 사용하지 말라

제네릭 클래스(제네릭 인터페이스) : 클래스와 인터페이스 선언에 타입 매개변수가 쓰이는 것을 말함

ex) List 는 List라고 쓰기도 한다

→ 이를 통틀어서 제네릭 타입이라고 한다

제네릭의 매개변수화 타입 → List 원소의 타입이 String인 리스트를 뜻함

제네릭 타입을 정의 하면 그에 딸린 raw type 도 함께 정의된다

raw 타입 : 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않은 때를 말함

//raw 타입 -> 불안전
private final Collection stamps = 

//매개변수화된 컬렉션 타입 -> 안전성 확보
private final Collection<Stamp> stamps = 

raw 타입으로 정의 하면 잘못된 타입의 객체를 넣었을시 해당 객체 호출 때 까지는 모름

→ 런타임에 문제 발생!!

반면 매개변수회된 컬렉션 타입은 컴파일시 오류발생하여 확인 가능

비한정적 와일드 타입 <?>

어떤 타입이라도 담을 수 있는 범용적 매개변수화 타입이다→ 제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경쓰지 않고 싶을 때 사용

static int numElementsInCommon(set<?> s1, set<?>s2)

raw 타입으로 써야하는 경우

  1. 클래스 리터럴에는 로타입으로 써야 한다

    List.class, String[].class (o)
    
    List<String>.class, List<?>.class (x)
  1. instance of 연산자와 함께 쓸때
if (o instanceof Set){ //로타입
	Set<?> s = (Set<?>) o //와일드카드 타입

제네릭 용어 정리

27. 비검사 경고를 제거하라

경고를 없엔다는 것 == 타입안정성을 확보하는것!

경고를 없엘 수 없지만 타입안전이 확실하다면

@Suppress Warings(”uncheked”)

를 달아 경고를 없에자

28. 배열보다는 리스트를 사용해라

배열과 제네릭 타입의 차이

  1. 배열은 공변이다(공변 = 함께 변한다), 그러나 제네릭은 불공빈이다

    1. super[]의 하위타입은 sub[]이다
    2. List 과 List는 아무런 관계가 없다
  2. 배열은 실체화가 된다

    1. 배열은 런타임에도 자신이 담기로한 원소의 타입을 인지 및 확인함
    2. 제네릭 타입정보는 런타임에 소거됨
    3. 따라서 제네릭 배열은 생성 불가함(타입 안전하지 않기 때문)
    List<String>[] stringLists = new List<String>[]
    이런게 안된다는 뜻이다!

29. 이왕이면 제네릭 타입으로 만들어라

제네릭 배열 생성 오류를 해결하는 방법

  1. 제네릭 배열 생성을 금지하는 제약을 우회함

    @SuppressWarings("unchecked")
    public Stack() {
    	elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
      
  2. elements 필드의 타입을 E[] → Object[]로 바꿈

    public E Pop() {
    	if (size == 0)
    		throw new EMtpyStackException();
    
    @SuppressWarnigs("unchekd")
    E result = (E) elemets[--size];
    elemtns[size] = null;
    return result;
    }

30. 이왕이면 제네릭 메서드로 만들라

제네릭 메서드

public static <E> Set<E> union(Set<E> s1, Set<E> s2> {
	Set<E> result = new HashSet<>(s1);
	result.addAll(s2);
	return result;
}

제네릭 싱글턴 팩터 : 타입 매개변수에 맞게 매번 그 객체의 타입을 바꿔주는 정적 팩터러

private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;

@SUppressWarnings("unchecked")
public static <T> UnaryOperator<T> identityFunction() {
	return (UnaryOperator<T>) IDENTITY_FN;
}

항등함수란 입력값을 수정없이 그대로 반환하는 특별함수이다

따라서 T가 어떤 타입이든 UnaryOperator를 사용해도 타입 안전하다

재귀적 한정 : 자기 자신이 들어간 표현식을 사용하여 타입 매개변수 허용범위를 한정할 수 있다.

public interface Comparalbe<T> {
	int compareTo(T o);
}

여기서 매개면수 T는 Comparable를 구현한 타입이 비교할 수 있는 원소의 타입을 정의함

다음은 재귀적 타입한정을 이용해 상호 비교할 수 있음을 표현하는 코드

public static <E extends Comparable<E>> E max(Collection<E> c);
<E extends Comparable<E>> 는 모든 타입 E는 자신과 비교할 수 있다 라는 뜻

31. 한정적 와일드카드를 사용해 API 유연성을 높여라

와일드 카드 공식

PECS(팩스) : Producer - extends, Consuemr -super

매개변수화 타입 T가

생산자일 경우

<? extends T>

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

소비자라면

 <? super T>

 //예시
 public void popAll(Collection<? super E> dst {
  while (!isEmtpy())
   dst.add(pop());
 }

이를 갯풋 원칙이라고 부른다

매개변수 : 메서드 선언에 정의한 변수

인자 : 메서드 호출시 넘기는 실제값

 void add(int value)
 add(10)
 
 value : 매개변수
 10 : 인자
 
 이를 제네릭으로 확장하면
 
 class set<T>
 Set<Integer>

32. 제네릭과 가변인수를 함께 쓸 때는 신중하라

제네릭 배열 매개변수에 값을 저장하는 것은 안전하지 않다

@SafeVarargs는 메서드 작성자가 그 메서드가 타입 안전함을 보장하는 장치이다.

33. 타입 안전 이종 컨테이너를 고려해라

타입 안전 이종 컨테이너 패턴 : 컨테이너 대신 키를 매개변수환 후 컨테이너에 값을 넣거나 뺼 때 매개변수화한 키를 함께 제공함 → 제네릭 타입 시스템이 값의 타입이 키와 같음을 보장

타입 안전 이종 컨테이너 패턴 API

public class Favorites {
	public <T> void putFavorite(Calss<T> type, T instance);
	public <T> T getFavorite(Class<T> type);
}

타입 안전 이종 컨테이너 패턴 - 클라이언트

public static void main(String[] args){
	Favorites f = new Favorites();
	
	f.putFavorite(String.class, "JAVA");

	String favoriteString = f.getFavorite(String.class);
	
	System.out.printf("%s", favoriteString);
}

0개의 댓글