Chapter 5 μ œλ„€λ¦­

Item 26 둜 νƒ€μž…μ€ μ‚¬μš©ν•˜μ§€ 말라

μ œλ„€λ¦­ νƒ€μž…μ€ 각각 일련의 λ§€κ°œλ³€μˆ˜ν™” νƒ€μž… (parameterized type) 을 μ •μ˜ ν•œλ‹€.

μ œλ„€λ¦­ νƒ€μž…μ„ ν•˜λ‚˜ μ •μ˜ν•˜λ©΄ 그에 λ”Έλ¦° 둜 νƒ€μž… (row type) 도 ν•¨κ»˜ μ •μ˜λœλ‹€.
μ΄λŠ” μ œλ„€λ¦­ νƒ€μž…μ—μ„œ νƒ€μž… λ§€κ°œλ³€μˆ˜λ₯Ό μ „ν˜€ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” λ•Œλ₯Ό λ§ν•œλ‹€. 둜 νƒ€μž…μ€ νƒ€μž… μ„ μ–Έμ—μ„œ μ œλ„€λ¦­ νƒ€μž… 정보가 μ „λΆ€ μ§€μ›Œμ§„ κ²ƒμ²˜λŸΌ λ™μž‘ν•˜λŠ”λ°,
μ œλ„€λ¦­μ΄ λ„λž˜ν•˜κΈ°μ „μ— μ „ μ½”λ“œμ™€ ν˜Έν™˜λ˜λ„λ‘ ν•˜κΈ° μœ„ν•œ ꢁ여지책이닀.

μ œλ„€λ¦­μ—μ„œ 둜 νƒ€μž…μ€ μ œλ„€λ¦­μ΄ μ•ˆκ²¨μ£ΌλŠ” μ•ˆμ •μ„±κ³Ό ν‘œν˜„λ ₯을 λͺ¨λ‘ 일게 되기 λ•Œλ¬Έμ— μ‚¬μš©μ΄ μ§€μ–‘λ˜λ‚˜
μ œλ„€λ¦­ λ“±μž₯ 이전에 μ‚¬μš©ν–ˆλ˜ μ½”λ“œλ“€μ΄ μ œλ„€λ¦­μ„ μ§€μ›ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ—
둜 νƒ€μž…μ„ μ§€μ›ν•˜κ²Œλ” λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ ν˜Έν™˜μ„±μ„ μœ„ν•΄ 둜 νƒ€μž…μ„ μ§€μ›ν•˜κ³ 
μ œλ„€λ¦­μ˜ κ΅¬ν˜„μ—λŠ” μ†Œκ±° (erasure) 방식을 μ‚¬μš©ν•˜κΈ°λ‘œ ν–ˆλ‹€.

Item 27 비검사 κ²½κ³ λ₯Ό μ œκ±°ν•˜λΌ

μ œλ„€λ¦­ μ‚¬μš©μ‹œ λ§Žμ€ 컴파일러 κ²½κ³ λ₯Ό μ œκ±°ν•΄μ•Ό ν•œλ‹€.

// Runtime Error
Set<Lark> exaltation = new HashSet();

// Success
Set<Lark> exaltation = new HashSet<>();

JDK 7 λΆ€ν„° μ§€μ›ν•˜λŠ” 닀이아λͺ¬λ“œ μ—°μ‚°μž (<>) λ₯Ό μ‚¬μš©ν•˜μ—¬ νƒ€μž… 좔둠을 μ‚¬μš©ν•˜μ—¬ 비검사 κ²½κ³ λ₯Ό μ œκ±°ν•˜μž.

λͺ¨λ“  λΉ„ 검사 κ²½κ³ λ₯Ό μ œκ±°ν•˜λ©΄ νƒ€μž… μ•ˆμ •μ„±μ΄ 보μž₯되며 런 νƒ€μž„μ‹œμ— ClassCaseException 이 λ°œμƒν•  일이 μ—†λ‹€.

κ²½κ³ λ₯Ό μ œκ±°ν•  수 μ—†μ§€λ§Œ νƒ€μž…μ΄ μ•ˆμ „ν•˜λ‹€κ³  ν™•μ‹  ν•œλ‹€λ©΄ @SuppressWarnings("unchecked") Annotation 을 μ‚¬μš©ν•˜μ—¬ κ²½κ³ λ₯Ό 숨기자.

@SuppressWarnings("unchecked") Annotation 을 μ‚¬μš©ν• λ•ŒλŠ” κ·Έ κ²½κ³ λ₯Ό λ¬΄μ‹œν•΄λ„ μ•ˆμ „ν•œ κ·Όκ±°λ₯Ό μ£Όμ„μœΌλ‘œ λͺ…μ‹œν•΄μ€˜μ•Ό ν•œλ‹€.

Item 28 λ°°μ—΄λ³΄λ‹€λŠ” 리슀트λ₯Ό μ‚¬μš©ν•˜λΌ

μ•„λž˜ μ½”λ“œλŠ” RuntimeException 을 λ°˜ν™˜ν•œλ‹€.

Object[] objAry = new Long[1];

// ArrayStoreException 을 λ°˜ν™˜
objAry[0] = "νƒ€μž…μ΄ 달라 넣을 수 μ—†λ‹€.";

μ΄λŠ” 배열이 κ³΅λ³€νƒ€μž…μ΄λΌ Long 용 μ €μž₯μ†Œμ— String 을 λ„£μ„μˆ˜ μ—†μ§€λ§Œ μ»΄νŒŒμΌμ‹œμ—λŠ” μ•Œμˆ˜ μ—†λ‹€.

// ν˜Έν™˜λ˜μ§€ μ•ŠλŠ” νƒ€μž…
List<Object> = objList = new ArrayList<Long>();

objList.add("νƒ€μž…μ΄ 달라 넣을 수 μ—†λ‹€.");

μœ„ μ½”λ“œλŠ” μ»΄νŒŒμΌμ‹œμ— 였λ₯˜λ₯Ό 검증 ν•  수 μžˆλ‹€.

λ˜ν•œ 배열은 싀체화 (reify) λœλ‹€.
μ΄λŠ” 배열은 λŸ°νƒ€μž„μ—λ„ μžμ‹ μ΄ λ‹΄κΈ°λ‘œ ν•œ μ›μ†Œμ˜ νƒ€μž…μ„ μΈμ§€ν•˜κ³  확인 ν•œλ‹€. κ·Έλž˜μ„œ κ³΅λ³€μž„μ—λ„ λΆˆκ΅¬ν•˜κ³  λŸ°νƒ€μž„μ‹œμ— μ„œλ‘œ λ‹€λ₯Έ νƒ€μž…μ„ μ‚½μž…ν•˜μ˜€μ„λ•Œ ArrayStoreException 이 λ°œμƒν•˜λŠ”κ²ƒμ΄λ‹€.

이와 λ°˜λŒ€λ‘œ μ œλ„€λ¦­μ€ λŸ°νƒ€μž„μ‹œμ—λŠ” 데이터 νƒ€μž…μ΄ μ†Œκ±°κ°€ λ˜λŠ” Type Erasure ν˜„μƒμ΄ λ°œμƒλœλ‹€. μ›μ†Œνƒ€μž…μ„ μ»΄νŒŒμΌμ‹œμ—λ§Œ κ²€μ‚¬ν•˜λ©° λŸ°νƒ€μž„μ—λŠ” μ•Œ 수 μ—†λ‹€λŠ” λœ»μ΄λ‹€.
이 Type Erasure λŠ” μ œλ„ˆλ¦­μ΄ μ§€μ›λ˜κΈ° μ „μ˜ λ ˆκ±°μ‹œ μ½”λ“œμ™€ μ œλ„€λ¦­ νƒ€μž…μ„ 순쑰둭게 μ‚¬μš© κ°€λŠ₯ν•˜λ„λ‘ ν•˜λŠ” μΌμ’…μ˜ λ§€μ»€λ‹ˆμ¦˜μœΌλ‘œ μ—­ν™œμ„ ν•΄μ€€λ‹€.

μ‚¬μš©λΆˆκ°€ μ œλ„€λ¦­ μœ ν˜•

  • μ œλ„€λ¦­ νƒ€μž… :: new List<E>[]
  • λ§€κ°œλ³€μˆ˜ν™” νƒ€μž… :: new List<String>[]
  • νƒ€μž… λ§€κ°œλ³€μˆ˜ :: new E[]

μœ„ 세가지 μœ ν˜•μ€ νƒ€μž…μ΄ μ•ˆμ „ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ œλ„€λ¦­ 배열을 λ§Œλ“€ 수 μ—†λ‹€.

μ΄λŠ” E List<E> List<String> 와 같은 νƒ€μž…μ„ 싀체화 λΆˆκ°€ νƒ€μž… (non-reifiable type) 이라고 ν•˜λŠ”λ° 싀체화 λ˜μ§€ μ•Šμ•„μ„œ λŸ°νƒ€μž„μ‹œ 컴파일 ν• λ•Œλ³΄λ‹€ μ •λ³΄λŸ‰μ΄ 적게 κ°€μ§€λŠ” νƒ€μž… 을 λ§ν•œλ‹€.
μ†Œκ±° λ§€μ»€λ‹ˆμ¦˜ λ•Œλ¬Έμ— λ§€κ°œλ³€μˆ˜ν™” νƒ€μž… κ°€μš΄λ° 싀체화 될수 μž‡λŠ” νƒ€μž…μ€ List<?> 와 Map<?, ?> 같은 λΉ„ ν•œμ • μ™€μΌλ“œμΉ΄λ“œ νƒ€μž… 뿐이닀.
배열을 λΉ„ν•œμ •μ  μ™€μΌλ“œμΉ΄λ“œ νƒ€μž…μœΌλ‘œ λ§Œλ“€μˆ˜ μžˆμ§€λ§Œ μœ μš©ν•˜κ²Œ μ“Έ 일이 거의 μ—†λ‹€.

싀체화 λΆˆκ°€ νƒ€μž…μ„ μ‚¬μš©ν• λ•ŒλŠ” μ•ˆμ •λœ κ°€λ³€μΈμˆ˜(varags) λΌλŠ” 의미둜 @SafeVarargs Annotation 을 μ‚¬μš©ν•˜μ—¬ λŒ€μ²΄ κ°€λŠ₯ν•˜λ‹€.

μƒμ„±μžμ—μ„œ μ»¬λ ‰μ…˜μ„ 받은 Chooser 클래슀 λ¦¬νŽ™ν† λ§

public class Chooser {
  private final Object[] choiceArray;

  public Chooser(Collection choices) {
    choiceArray = choices.toArray();
  }

  public Object choose() {
    Random rnd = ThreadLocalRandom.current();
    return choiceArray[rnd.nextInt(choiceArray.length)];
  }
}

choose() λ©”μ„œλ“œλ₯Ό 호좜 ν• λ•Œλ§ˆλ‹€ λ°˜ν™˜λœ Objectλ₯Ό μ›ν•˜λŠ” νƒ€μž…μœΌλ‘œ ν˜• λ°˜ν™˜ ν•΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— νƒ€μž…μ΄ λ‹€λ₯Έ μ›μ†Œκ°€ λ“€μ–΄μžˆμœΌλ©΄ ν˜• λ³€ν™˜ 였λ₯˜κ°€ λ‚œλ‹€
λ•Œλ¬Έμ— ν•΄λ‹Ή 클래슀λ₯Ό μ œλ„€λ¦­μœΌλ‘œ λ³€ν™˜ν•œλ‹€.




Β 
Β 
Β 




public class Chooser<T> {
  private final T[] choiceArray;

  public Chooser(Collection<T> choices) {
    choiceArray = choiceArray.toArray();
  }

  // choose λ©”μ„œλ“œλŠ” κ·ΈλŒ€λ‘œ
}

Object[] κ°€ T[] 으둜 λ³€ν™˜ λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— λͺ…μ‹œμ μœΌλ‘œ μΊμŠ€νŒ… ν•΄μ€€λ‹€.





Β 





public class Chooser<T> {
  private final T[] choiceArray;

  public Chooser(Collection<T> choices) {
    choiceArray = (T[]) choiceArray.toArray();
  }

  // choose λ©”μ„œλ“œλŠ” κ·ΈλŒ€λ‘œ
}

T[] κ°€ 무슨 νƒ€μž…μΈμ§€ μ•Œ 수 μ—†μœΌλ‹ˆ ν˜• λ³€ν™˜μ΄ λŸ°νƒ€μž„μ— μ•ˆμ „ν•¨μ„ 보μž₯ν• μˆ˜ μ—†λ‹€λŠ” κ²½κ³ κ°€ λœ¬λ‹€.

비검사 ν˜• λ³€ν™˜ 경고이기 λ•Œλ¬Έμ— λ°°μ—΄ λŒ€μ‹  리슀트λ₯Ό μ‚¬μš©ν•œλ‹€.





Β 




Β 



public class Chooser<T> {
  private final List<T> choiceList;

  public Chooser(Collection<T> choices) {
    choiceList = new ArrayList<>(choices);
  }

  public T choose() {
    Random rnd = ThreadLocalRandom.current();
    return choiceList.get(rnd.nextInt(choiceList.size()));
  }
}

μ½”λ“œλŸ‰μ€ μ‘°κΈˆλ” λŠ˜μ—ˆκ³  μ•„λ§ˆλ„ μ‘°κΈˆλ” λŠλ¦¬μ§€λ§Œ,
λŸ°νƒ€μž„μ— ClassCastException 이 λ°œμƒλ  λ¦¬μŠ€ν¬κ°€ μ€„μ—ˆλ‹€.

핡심 정리

배열은 곡변 이고 싀체화 λ˜λŠ” 반면,
μ œλ„€λ¦­μ€ λΆˆκ³΅λ³€ 이고 νƒ€μž… 정보가 μ†Œκ±° λœλ‹€.

배열은 λŸ°νƒ€μž„μ—λŠ” νƒ€μž…μ΄ μ•ˆμ „ ν•˜μ§€λ§Œ 컴파일 νƒ€μž„μ—λŠ” μ•ˆμ „ν•˜μ§€ μ•Šλ‹€.
μ œλ„€λ¦­μ€ 이와 λ°˜λŒ€ μ—¬μ„œ κ·Έ λ‘˜μ„ μ„žμ–΄ μ“°κΈ°λž€ 쉽지 μ•Šλ‹€.

λ‘˜μ„ μ„žμ–΄ μ“°λ‹€ 컴파일 였λ₯˜λ‚˜ κ²½κ³ λ₯Ό λ§Œλ‚˜λ©΄ κ°€μž₯ λ¨Όμ € 배열을 리슀트둜 λŒ€μ²΄ν•˜λŠ” 방법을 적용 ν•΄λ³΄μž.

Item 29 이왕이면 μ œλ„€λ¦­ νƒ€μž…μœΌλ‘œ λ§Œλ“€λΌ

ν΄λΌμ΄μ–ΈνŠΈμ—μ„œλŠ” 직접 ν˜• λ³€ν™˜ν•΄μ•Ό ν•˜λŠ” νƒ€μž…λ³΄λ‹€ μ œλ„€λ¦­ νƒ€μž…μ΄ 더 μ•ˆμ „ν•˜κ³  μ“°κΈ° νŽΈν•˜λ‹€.

μƒˆλ‘œμš΄ νƒ€μž…μ„ 섀계할 λ•ŒλŠ” ν˜• λ³€ν™˜ 없이도 μ‚¬μš©ν•  수 μžˆλ„λ‘ μ œλ„€λ¦­ νƒ€μž…μœΌλ‘œ λ§Œλ“€μ–΄μ•Ό ν•  κ²½μš°κ°€ λ§Žλ‹€.

λ‹€μŒμ€ Stack ν΄λž˜μŠ€λŠ” μ œλ„€λ¦­ νƒ€μž…μœΌλ‘œ λ¦¬νŽ™ν† λ§ ν•˜λŠ” 과정이닀.

public class Stack {
  private Object[] elements;
  private int size = 0;
  private static final int DEFAULT_INITIAL_CAPACITY = 16;

  public Stack() {
    elements = new Object[DEFAULT_INITAL_CAPACITY];
  }

  public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
  }

  public Object pop() {
    if (size == 0)
      throw new EmptyStackException();
    Object result = elements[--size];

    // μ‚¬μš©μ΄ λλ‚œ μ°Έμ‘°λ₯Ό ν•΄μ œ
    elements[size] = null;

    return result;
  }

  public boolean isEmpty() {
    return size == 0;
  }

  private void ensureCapacity() {
    if (elements.length == size)
      elements = Arrays.copyOf(elements, 2 * size + 1);
  }
}

Object λ₯Ό μ œλ„€λ¦­ νƒ€μž…μœΌλ‘œ λ³€κ²½ν•œλ‹€.


Β 




Β 







Β 


Β 








public class Stack<E> {
  private E[] elements;
  private int size = 0;
  private static final int DEFAULT_INITAL_CAPACITY = 16;

  public Stack() {
    elements = new E(DEFAULT_INITAL_CAPACITY);
  }

  public void push(E e) {
    ensureCapacity();
    elements[size++] = e;
  }

  public E pop() {
    if (size == 0)
      throw new EmptyStackException();
    E result = elements[--size];

    // μ‚¬μš©μ΄ λλ‚œ μ°Έμ‘° ν•΄μ œ
    elements[size] = null;

    return result;
  }
}

μ•„λž˜μ™€ 같은 E λŠ” 싀체화 λΆˆκ°€ νƒ€μž…μœΌλ‘œ 배열을 λ§Œλ“€μˆ˜ μ—†μ–΄ μ•„λž˜μ™€ 같은 μ—λŸ¬λ₯Ό λ±‰λŠ”λ‹€.


Β 

Stack.java:8: generic array creation
  elements = new E[DEFAULT_INITIAL_CAPACITY];

이 κ²½κ³ λŠ” 비검사 ν˜•λ³€ν™˜μ΄ ν”„λ‘œκ·Έλž¨μ˜ μ•ˆμ •μ„±μ„ ν•΄μΉ μˆ˜ μžˆλ‹€λŠ” μ˜λ―Έμ΄λ―€λ‘œ 슀슀둜 확인해야 ν•œλ‹€.

  • λ°°μ—΄μ˜ elements λŠ” private ν•„λ“œμ— μ €μž₯λœλ‹€.
  • Client 둜 λ°˜ν™˜λ˜κ±°λ‚˜ λ‹€λ₯Έ λ©”μ„œλ“œμ— μ „λ‹¬λ˜λŠ” 일이 μ „ν˜€ μ—†λ‹€.
  • push() λ©”μ„œλ“œλ₯Ό 톡해 배열에 μ €μž₯λ˜λŠ” μ›μ†Œμ˜ νƒ€μž…μ€ 항상 E λ‹€.

항상 μœ„ 3가지 쑰건을 λ§Œμ‘±ν•˜λ―€λ‘œ 이 μ½”λ“œμ˜ 비검사 ν˜•λ³€ν™˜μ€ μ•ˆμ „ν•˜λ‹€.

μ•„λž˜μ™€ 같이 μ œλ„€λ¦­ λ°°μ—΄ 생성을 κΈˆμ§€ν•˜λŠ” μ œμ•½μ„ μš°νšŒν•˜λŠ” λ°©λ²•μœΌλ‘œ
Object 배열을 μƒμ„±ν•œ λ‹€μŒ μ œλ„€λ¦­ λ°°μ—΄λ‘œ ν˜•λ³€ν™˜ ν•˜λŠ” 방법이 μžˆλ‹€.
ν•˜μ§€λ§Œ μ΄λŠ” 였λ₯˜λŒ€μ‹  κ²½κ³  λ₯Ό 내보낸닀.

μ•„λž˜ μ½”λ“œμ™€ 같이 μƒμ„±μž λ©”μ„œλ“œλ₯Ό @SuppressWarnings("unchecked") μ• λ„ˆν…Œμ΄μ…˜μœΌλ‘œ ν•΄λ‹Ή κ²½κ³ λ₯Ό μˆ¨κΈ΄λ‹€.
λ°°μ—΄ elements 은 push(E) 둜 λ„˜μ–΄μ˜¨ μΈμŠ€ν„΄μŠ€ E 만 λ‹΄λŠ”λ‹€.

@SuppressWarnings("unchecked")
public Stack() {
  elements = (E[]) new Object[DEFAULT_INITAL_CAPACITY];
}

μœ„ μ½”λ“œλŠ” νƒ€μž…μ˜ μ•ˆμ •μ„±μ„ 보μž₯ν•˜μ§€λ§Œ 이 λ°°μ—΄μ˜ λŸ°νƒ€μž…μ€ E[] κ°€ μ•„λ‹Œ Object[] 이닀.

μ•„λž˜μ™€ 같이 λ°°μ—΄ 객체 elements ν˜•λ³€ν™˜ ν•  수 μ—†λ‹€λŠ” μ—λŸ¬κ°€ λœ¬λ‹€.

Stack.java:19: incompatible types found: Object, required: E
  E result = elements[--size];

배열이 λ°˜ν™˜ν•œ μ›μ†Œλ₯Ό E 둜 ν˜•λ³€ν™˜ ν•˜λ©΄ 였λ₯˜λŒ€μ‹  κ²½κ³ κ°€ λœ¬λ‹€.

Stack.java:19: warning: [unchecked] unchecked cast found: Object, required: E
  E result = (E) elements[--size];

μœ„ μ½”λ“œμ—μ„œμ˜ κ²½κ³  E λŠ” 싀체화 λΆˆκ°€ νƒ€μž…μ΄λ―€λ‘œ λŸ°νƒ€μž„μ— μ΄λ£¨μ–΄μ§€λŠ” ν˜•λ³€ν™˜μ΄ μ•ˆμ „ν•œμ§€ 증λͺ…ν•  방법이 μ—†λ‹€.
ν•˜μ§€λ§Œ push() λ©”μ„œλ“œμ—μ„œλŠ” E νƒ€μž…λ§Œ ν—ˆμš©ν•˜λ―€λ‘œ μœ„ ν˜•λ³€ν™˜μ€ μ•ˆμ „ν•˜λ‹€.

ν˜•λ³€ν™˜μ΄ μ•ˆμ „ν•˜λ‹€ 생각이 λ“€λ©΄ 비검사 κ²½κ³ λ₯Ό μˆ¨κΈ΄λ‹€.

public E pop() {
  if (size == 0)
    throw new EmptyStackException();

  @SuppressWarnings("unchecked")
  E result = (E) elements[--size];

  // μ‚¬μš©μ΄ λλ‚œ μ°Έμ‘°λ₯Ό ν•΄μ œ
  elements[size] = null;
  return result;
}

Item 28 μ—μ„œμ˜ λ°°μ—΄λ³΄λ‹€λŠ” 리슀트λ₯Ό μš°μ„ ν•˜λΌλŠ” 상황에 따라 λ‹€λ₯΄λ©°
μ œλ„€λ¦­ νƒ€μž… μ•ˆμ—μ„œ 리슀트λ₯Ό μ‚¬μš©ν•˜λŠ”κ²Œ 항상 κ°€λŠ₯ν•˜μ§€λ„ 더 쒋은 방법이 μ•„λ‹μˆ˜λ„ μžˆλ‹€.

μžλ°”κ°€ 리슀트λ₯Ό κΈ°λ³Ένƒ€μž…μœΌλ‘œ μ œκ³΅ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ ArrayList 와 같은 μ œλ„€λ¦­ νƒ€μž…λ„ 결ꡭ은 κΈ°λ³Έ νƒ€μž…μΈ 배열을 μ‚¬μš©ν•΄ κ΅¬ν˜„ν•΄μ•Ό ν•˜λŠ” κ²½μš°λ„ μžˆλ‹€.

HashMap κ³Ό 같은 μ œλ„€λ¦­ νƒ€μž…μ€ μ„±λŠ₯을 높일 λͺ©μ μœΌλ‘œ 배열을 μ‚¬μš©ν•˜κΈ°λ„ ν•œλ‹€.

μ œλ„€λ¦­ νƒ€μž…μ€ νƒ€μž… λ§€κ°œλ³€μˆ˜μ— μ–΄λ– ν•œ μ œμ•½μ„ 두고 μžˆμ§€λŠ” μ•Šμ§€λ§Œ κΈ°λ³Ένƒ€μž…μ€ μ‚¬μš©ν•  수 μ—†λ‹€.
κ°€λ Ή Stack<int> Stack<double> 을 λ§Œλ“œλ €κ³  ν•˜λ©΄ 컴파일 였λ₯˜κ°€ λ‚œλ‹€.
μ΄λŠ” μžλ°”μ˜ κ·Όλͺ¬μ μΈ λ¬Έμ œμ΄λ‚˜, λ°•μ‹±λœ κΈ°λ³Ένƒ€μž…μ„ μ‚¬μš©ν•˜μ—¬ μš°νšŒκ°€ κ°€λŠ₯ν•˜λ‹€.

ν˜Ήμ€ νƒ€μž… λ§€κ°œλ³€μˆ˜μ˜ μ œμ•½μ„ 두어 μ‚¬μš©ν•˜λŠ” 방법도 μžˆλ‹€. (ν•œμ •μ  νƒ€μž… λ§€κ°œλ³€μˆ˜) 예λ₯Ό λ“€λ©΄ java.util.concurrent.DelayQueue κ³Ό 같이 DelayQueue μžμ‹ κ³Ό DelayQueue 의 μ›μ†Œλ₯Ό μ‚¬μš©ν•˜λŠ” Delayed ν΄λž˜μŠ€μ—μ„œ ClassCastException κ±±μ • ν•  ν•„μš” 없이 μ‚¬μš© κ°€λŠ₯ν•˜λ‹€.

Item 30 이왕이면 μ œλ„€λ¦­ λ©”μ„œλ“œλ‘œ λ§Œλ“€λΌ

ν΄λž˜μŠ€μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ λ©”μ„œλ“œλ„ μ œλ„€λ¦­μœΌλ‘œ λ§Œλ“€μˆ˜ μžˆλ‹€.

λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ λ°›λŠ” 정적 μœ ν‹Έλ¦¬ν‹° λ©”μ„œλ“œλŠ” μ œλ„€λ¦­μœΌλ‘œ λŒ€ν‘œμ μœΌλ‘œ Collections 의 μ•Œκ³ λ¦¬μ¦˜ λ©”μ„œλ“œλŠ” λͺ¨λ‘ μ œλ„€λ¦­ λ©”μ„œλ“œμ΄λ‹€.


Β 
Β 



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

μœ„ μ½”λ“œλŠ” μ»΄νŒŒμΌμ€ λ˜μ§€λ§Œ κ²½κ³ κ°€ λ‘κ°œκ°€ λ°œμƒν•œλ‹€.

Union.java:5: warning: [unchecked] unchecked call to HashSet(Collection<? extends E>) as a member of raw type HashSet
  Set result = new HashSet(s1);
Union.java:6: warning: [unchecked] unchecked call to addAll(Collection<? extends E>) as a member of raw type Set
  result.addAll(s2);

μœ„ κ²½κ³ λŠ” λ©”μ„œλ“œ νƒ€μž…μ˜ μ•ˆμ •μ„±μ„ 보μž₯ν•΄μ•Ό ν•œλ‹€.

Β 
Β 




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

λ‹¨μˆœν•œ μ œλ„€λ¦­ λ©”μ„œλ“œλΌλ©΄ μœ„ μ½”λ“œμ •λ„κ°€ μ λ‹Ήν•˜λ‹€.
이 λ©”μ„œλ“œλŠ” κ²½κ³  없이 컴파일 되며, μ•ˆμ „ν•˜κ³  쓰기도 쉽닀.

public static void main(String[] args) {
  Set<String> guys = Set.of("ν†°", "λ”•", "해리");
  Set<String> stooges = Set.of("래리", "λͺ¨μ—", "컬리");
  Set<String> aflCio = union(guys, stooges);
  System.out.println(aflcio);
}

μœ„ ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰μ‹œμΌ°μ„λ•Œμ˜ κ²°κ³ΌλŠ” μ•„λž˜μ™€ κ°™λ‹€.

[λͺ¨μ—, ν†°, 해리, 래리, 컬리, λ”•]

ν•­λ“±ν•¨μˆ˜ λž€ μž…λ ₯값을 μˆ˜μ •μ—†μ΄ κ·ΈλŒ€λ‘œ λ°˜ν™˜ν•˜λŠ” νŠΉλ³„ν•œ ν•¨μˆ˜λ‘œ
이λ₯Ό μ΄μš©ν•œ μ œλ„€λ¦­ μ‹±κΈ€ν„΄ 방식λ₯Ό λ§Œλ“€λ©΄ μ•„λž˜μ˜ μ˜ˆμ‹œμ™€ κ°™λ‹€.



Β 




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

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

μœ„ 비검사 κ²½κ³  μ• λ„ˆν…Œμ΄μ…˜μ€ T κ°€ μ–΄λ–€ νƒ€μž…μ΄λ“  UnaryOperator<Object> λŠ” UnaryOperator<T> κ°€ μ•„λ‹ˆκΈ° λ•Œλ¬Έμ΄λ‹€ (νƒ€μž… μ†Œκ±°)

ν•˜μ§€λ§Œ ν•­λ“± ν•¨μˆ˜λŠ” T κ°€ μ–΄λ–€ νƒ€μž…μ΄λ“  μž…λ ₯ 값을 κ·ΈλŒ€λ‘œ λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ— UnaryOperator<T> λ₯Ό μ‚¬μš©ν•΄λ„ μ•ˆμ „ν•˜λ‹€.

Item 31 ν•œμ •μ  μ™€μΌλ“œμΉ΄λ“œλ₯Ό μ‚¬μš©ν•΄ API μœ μ—°μ„±μ„ 높이라

λ§€κ°œλ³€μˆ˜ν™” νƒ€μž… (Parameterized Types) 은 λΆˆκ³΅λ³€ (invariant) 이닀.

μ™€μΌλ“œμΉ΄λ“œ νƒ€μž…μ„ μ‚¬μš©ν•˜μ§€ μ•Šμ€ pushAll() λ©”μ„œλ“œλŠ” 결함이 μžˆλ‹€.

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

Integer λŠ” Number 의 ν•˜μœ„νƒ€μž…μ΄λ‹ˆ 잘 λ™μž‘ ν•΄μ•Ό ν•œλ‹€. (λ¦¬μŠ€μ½”ν”„ μΉ˜ν™˜ 법칙)



Β 

Stack<Number> numberStack = new Stack();
Iterable<Integer> integers = ...;
numberStack.pushAll(integers);

ν•˜μ§€λ§Œ μ•„λž˜μ™€ 같이 였λ₯˜ 메세지가 λœ¬λ‹€.
λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ΄ λΆˆκ³΅λ³€μ΄κΈ° λ•Œλ¬Έ 이닀.

StackTest.java:7: error: incompatible types: Iterable<Integer> cannot be converted to Iterable<Number>
  numberStack.pushAll(integers);

μœ„ μ—λŸ¬λ₯Ό 고치기 μœ„ν•΄μ„œ μžλ°”λŠ” ν•œμ •μ  μ™€μΌλ“œ μΉ΄λ“œ νƒ€μž…μ΄λΌλŠ” νŠΉλ³„ν•œ λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ 지원 ν•œλ‹€.
pushAll() 의 μž…λ ₯ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ€ E 의 Iterable 이 μ•„λ‹ˆλΌ E 의 ν•˜μœ„ νƒ€μž…μ˜ Iterable 이여야 ν•˜λ©° μ™€μΌλ“œ μΉ΄λ“œμ˜ νƒ€μž…μ€ Iterable<? extends E> 이 λ˜μ–΄μ•Ό ν•œλ‹€.

μƒμ‚°μž (Producer) 에 μ˜ν•œ λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ 적용

Β 





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

popAll() λ©”μ„œλ“œ λ˜ν•œ μ™€μΌλ“œμΉ΄λ“œμ˜ 미적용으둜 인해 결함이 생긴닀.

Stack<Number> numberStack = new Stack<>();
Collection<Object> objects = ...;
numberStack.popAll(objects);

μœ„ μ½”λ“œλ₯Ό 컴파일 ν•˜λ©΄ Collection<Object> λŠ” Collction<Number> 의 ν•˜μœ„νƒ€μž…μ΄ μ•„λ‹ˆλ‹€ λΌλŠ” 메세지와 ν•¨κ»˜ 였λ₯˜κ°€ λ°œμƒν•œλ‹€.
이 κ²½μš°μ—λ„ ν•œμ •μ  μ™€μΌλ“œμΉ΄λ“œ νƒ€μž…μ„ μ μš©ν•˜μ—¬ ν•΄κ²°ν•œλ‹€.

popAll() 의 μž…λ ₯ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ΄ E 의 Collection 이 μ•„λ‹ˆλΌ E 의 μƒμœ„ νƒ€μž…μ˜ Collection 이여야 ν•˜λ©° μ™€μΌλ“œ μΉ΄λ“œμ˜ νƒ€μž…μ€ Collection<? super E> κ°€ λ˜μ–΄μ•Ό ν•œλ‹€.

μ†ŒλΉ„μž (Consumer) λ§€κ°œλ³€μˆ˜μ— μ™€μΌλ“œμΉ΄λ“œ νƒ€μž…μ„ 적용

Β 




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

PECS

μœ„μ™€ 같은 νŒ¨ν„΄μ„ PECS (Producer Extends & Consumer Super) 라고 ν•˜λ©° λ§€κ°œλ³€μˆ˜λ₯Ό μœ μ—°μ„±μžˆκ²Œ μ™€μΌλ“œμΉ΄λ“œλ₯Ό μ‚¬μš©ν•˜κ²Œλ” 도와쀀닀.
λ§€κ°œλ³€μˆ˜ν™” νƒ€μž… T 생산 및 μ†ŒλΉ„μ— 맞게 ν•œμ •μ  μ™€μΌλ“œ μΉ΄λ“œκ°€ λΆ„λ¦¬λœλ‹€.

  • μƒμ‚°μž : <? extends T>
  • μ†ŒλΉ„μž : <? super T>

Item 32 μ œλ„€λ¦­κ³Ό κ°€λ³€μΈμˆ˜λ₯Ό ν•¨κ»˜ μ“Έ λ•ŒλŠ” μ‹ μ€‘ν•˜λΌ

싀체화 λΆˆκ°€ νƒ€μž… (non-reifiable type) 은 λŸ°νƒ€μž„μ—λŠ” 컴파일 νƒ€μž„λ³΄λ‹€ νƒ€μž…κ΄€λ ¨ 정보λ₯Ό 적게 λ‹΄κ³  μžˆλ‹€.

λ©”μ„œλ“œλ₯Ό μ„ μ–Έν•  λ•Œ 싀체화 λΆˆκ°€ νƒ€μž…μœΌλ‘œ varargs λ§€κ°œλ³€μˆ˜λ₯Ό μ„ μ–Έν•˜λ©΄ μ»΄νŒŒμΌλŸ¬κ°€ κ²½κ³ λ₯Ό 보낸닀.

warning: [unchecked] Possible heap pollution from
  parameterized vararg type List<String>

λ§€κ°œλ³€μˆ˜ν™” νƒ€μž… (parameterized type) 의 λ³€μˆ˜κ°€ λ‹€λ₯Έ 객체λ₯Ό μ°Έμ‘°ν•˜λ©΄ νž™ (heap) μ˜€μ—Όμ΄ λ°œμƒν•œλ‹€λŠ” 의미둜
λ‹€λ₯Έ νƒ€μž…μ˜ 객체λ₯Ό μ°Έμ‘°ν•˜λŠ” μƒν™©μ—μ„œ μ»΄νŒŒμΌλŸ¬κ°€ μžλ™ μƒμ„±ν•œ ν˜•λ³€ν™˜μ΄ μ‹€νŒ¨ν•  수 μžˆλ‹€.

μ œλ„€λ¦­ κ°€λ³€μΈμˆ˜ (varargs) λ°°μ—΄ λ§€κ°œλ³€μˆ˜μ— 값을 μ €μž₯ν•˜λŠ”κ²ƒμ€ μ•ˆμ „ν•˜μ§€ μ•Šλ‹€.
μ•„λž˜ 예제λ₯Ό 보자




Β 
Β 


static void dangerous(List<String>... stringLists) {
  List<Integer> intList = List.of(42);
  Object[] objects = stringLists;
  objects[0] = intLists;            // νž™ μ˜€μ—Ό λ°œμƒ
  String s = stringLists[0].get(0)  // ClassCastException - ν˜• λ³€ν™˜ λ°œμƒ
}

μžλ°”μ—μ„œλŠ” μœ„ 같은 μœ„ν—˜μ„±μ„ μΈμ§€ν•˜κ³ λ„ λŒ€ν‘œμ μœΌλ‘œ μ•„λž˜μ™€ 같은 λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.

  • Array.asList(T... a)
  • Collections.addAll(Collection<? super T> c, T... elements)
  • EnumSet.of(E first, E... rest)

μ œλ„€λ¦­μ΄λ‚˜ ν˜Ήμ€ λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ˜ κ°€λ³€μΈμˆ˜ (varargs) νƒ€μž…μ˜ λ§€κ°œλ³€μˆ˜λ₯Ό λ°›λŠ” λͺ¨λ“  λ©”μ„œλ“œμ—μ„œλŠ” @SafeVarargs λ₯Ό λ‹¬μ•„μ€˜μ•Ό ν•œλ‹€.

μ΄λŠ” κ°€λ³€μΈμˆ˜ (varargs) νƒ€μž…μ˜ λ§€κ°œλ³€μˆ˜ 배열에 아무것도 μ €μž₯ν•΄μ„  μ•ˆλ˜λ©°, κ·Έ λ°°μ—΄ ν˜Ήμ€ λ³΅μ œλ³Έμ„ μ‹ λ’°ν•  수 μ—†λŠ” μ½”λ“œμ— λ…ΈμΆœν•˜λŠ” μ•ŠλŠ”κ²ƒμ΄ 원칙이닀.

Item 33 νƒ€μž… μ•ˆμ „ 이쒅 μ»¨ν…Œμ΄λ„ˆλ₯Ό κ³ λ €ν•˜λΌ

μ»¨ν…Œμ΄λ„ˆ λŒ€μ‹  ν‚€λ₯Ό λ§€κ°œλ³€μˆ˜ν™” μ‹œν‚€κ³ , μ»¨ν…Œμ΄λ„ˆμ— 값을 λ„£κ±°λ‚˜ λΊ„ λ•Œ λ§€κ°œλ³€μˆ˜ν™” ν•œ ν‚€λ₯Ό ν•¨κ»˜ μ œκ³΅ν•œλ‹€.

public class Favorites {
  public <T> void putFavorite(Class<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");
  f.putFavorite(Integer.class, 0xcafebabe);
  f.putFavorite(Class.class, Favorites.class);

  String favoriteString = f.getFavorite(String.class);
  int favoriteInteger = f.getFavorite(Integer.class);
  Class<?> favoriteClass = f.getFavorite(Class.class);

  System.out.println("%s %x%n", favoriteString, favoriteInteger, favoriteClass.getName());
}
Java cafebabe Favorites

Favorites μΈμŠ€ν„΄μŠ€λŠ” String 을 μš”μ²­ν–ˆλŠ”λ° Integer λ₯Ό λ°˜ν™˜ν•˜λŠ” 일은 μ ˆλŒ€ μ—†λ‹€.
μ΄λŠ” λͺ¨λ“  ν‚€μ˜ νƒ€μž…μ΄ 각각 λΆ„λ¦¬λ˜μ–΄ μžˆμ–΄ 일반적인 λ°±κ³Ό 달리 μ—¬λŸ¬κ°€μ§€ νƒ€μž…μ˜ μ›μ†Œλ₯Ό 담을 수 μžˆλ‹€.

λ”°λΌμ„œ Favorites 을 νƒ€μž… μ•ˆμ „ μ΄μ’…μ»¨ν…Œμ΄λ„ˆ 라고 ν•œλ‹€.

μ•„λž˜λŠ” Favorites 의 κ΅¬ν˜„μ΄λ‹€.

public class Favorites {
    private Map<Class<?>, Object> favorites = new HashMap<>();

    public <T> void putFavorite(Class<T> type, T instance) {
      favorites.put(Objects.requireNonNull(type), instance);
    }

    public <T> T getFavorite(Class<T> type) {
      return type.cast(favorites.get(type));
    }
}

참고자료

https://sjh836.tistory.com/171