Chapter 8 λ©”μ„œλ“œ

Item 49 λ§€κ°œλ³€μˆ˜κ°€ μœ νš¨ν•œμ§€ κ²€μ‚¬ν•˜λΌ

λ©”μ„œλ“œμ™€ μƒμ„±μž λŒ€λΆ€λΆ„μ€ μž…λ ₯ λ§€κ°œλ³€μˆ˜ 값이 νŠΉμ • 쑰건을 λ§Œμ‘±ν•˜κΈ°λ₯Ό λ°”λž€λ‹€.

  • 인덱슀 값은 음수λ₯Ό μ œμ™Έν•œ μ •μˆ˜μ΄λ‹€
  • 객체 μ°Έμ‘°λŠ” null 이 μ•„λ‹ˆμ—¬μ•Ό ν•œλ‹€

μœ„ 두가지 μ˜ˆκ°€ λŒ€ν‘œμ μΈ μ œμ•½ 사항이닀.

μ΄λŸ¬ν•œ μ œμ•½ 사항은 λ°˜λ“œμ‹œ λ¬Έμ„œν™” ν•΄μ•Ό ν•˜λ©° λ©”μ„œλ“œμ˜ body κ°€ μ‹€ν–‰ν•˜κΈ° 이전에 κ²€μ¦λ˜λŠ”κ²ƒμ΄ μ’‹λ‹€

λ©”μ„œλ“œ body κ°€ μ‹€ν–‰λ˜κΈ° 전에 λ§€κ°œλ³€μˆ˜κ°’μ„ ν™•μΈν•œλ‹€λ©΄ 잘λͺ»λœ 값이 λ„˜μ–΄μ™”μ„λ•Œ 즉각적이고 κΉ”λ”ν•œ λ°©μ‹μœΌλ‘œ μ˜ˆμ™Έλ₯Ό 던질수 μžˆλ‹€.

λ§€κ°œλ³€μˆ˜ 검사에 μ‹€νŒ¨ν•˜λ©΄ μ‹€νŒ¨ μ›μžμ„± (failure atomicity) 을 μ–΄κΈ°λŠ” κ²°κ³Ό λ₯Ό λ‚³μ„μˆ˜ μžˆλ‹€.

public BigInteger mod(BigInteger m) {
  if (m.signum() <= 0) {
    throw new ArithmeticException("κ³„μˆ˜(m)λŠ” μ–‘μˆ˜μ—¬μ•Ό ν•©λ‹ˆλ‹€. " + m);
  }

  /* statement */
}

μœ„ λ©”μ„œλ“œλŠ” m 이 null 이면 m.signum() 을 ν˜ΈμΆœν•˜λ©΄ NPE (Null Pointer Exception) 을 λ˜μ§„λ‹€.

ν•˜μ§€λ§Œ NPE 검사λ₯Ό μˆ˜ν–‰ν•œλ‹€λŠ” 말은 μ „ν˜€ μ—†μ§€λ§Œ BigInteger 클래슀 κ΄€μ μ—μ„œ κΈ°μˆ ν•˜μ˜€μœΌλ‹ˆ λ¬Έμ œκ°€ μ—†λ‹€.

Item 50 μ μ‹œμ— 방어적 볡사본을 λ§Œλ“€λΌ

ν΄λΌμ΄μ–ΈνŠΈκ°€ λΆˆλ³€μ‹μ„ κΉ¨λ“œλ¦¬λ €κ³  ν˜ˆμ•ˆμ΄ λ˜μ–΄ μžˆλ‹€λŠ” κ°€μ •μ•„ν•˜μ— λ°©μ–΄μ μœΌλ‘œ ν”„λ‘œκ·Έλž˜λ° ν•΄μ•Ό ν•œλ‹€.

public final class Period {
  private final Date start;
  private final Date end;

  public Period(Date start, Date end) {
    if (start.compareTo(end) > 0) {
      throw new IllegalArgumentException(start + " after " + end);
    }

    this.start = start;
    this.end = end;
  }

  public Date start() {
    return start;
  }

  public Date end() {
    return end;
  }
}

μœ„ μ½”λ“œμ—μ„œ λ‚΄λΆ€λ₯Ό μˆ˜μ •ν•˜λ €λ©΄ λ‹€μŒκ³Ό 같이 μž‘μ„± κ°€λŠ₯ν•˜λ‹€.




Β 

Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78);

μ™ΈλΆ€ 곡격으둜 Period μΈμŠ€ν„΄μŠ€ λ‚΄λΆ€λ₯Ό λ³΄ν˜Έν•˜λ €λ©΄ μƒμ„±μžμ—κ²Œ 받은 κ°€λ³€ λ§€κ°œλ³€μˆ˜λ₯Ό 각각 λ°©μ–΄μ μœΌλ‘œ 볡사 (Defensive Copy) ν•΄μ•Ό ν•œλ‹€.

μ΄ν›„μ—λŠ” Period μΈμŠ€ν„΄μŠ€ μ•ˆμ—μ„œλŠ” 원본이 μ•„λ‹Œ 볡사본을 ν—ˆμš©ν•΄μ•Ό ν•œλ‹€.


Β 
Β 






public Period(Date start, Date end) {
  this.start = new Date(start.getTime());
  this.end = new Date(end.getTime());

  if (this.start.compareTo(this.end) > 0) {
    throw new IllegalArgumentException(this.start + " after " + this.end);
  }
}

λ§€κ°œλ³€μˆ˜μ˜ μœ νš¨μ„±μ„ κ²€μ‚¬ν•˜κΈ° 전에 방어적 볡사본을 λ§Œλ“€κ³ , 이 λ³΅μ‚¬λ³ΈμœΌλ‘œ μœ νš¨μ„±μ„ κ²€μ‚¬ν•œλ‹€.

λ§€κ°œλ³€μˆ˜κ°€ 제 3μžμ— μ˜ν•΄ ν™•μž₯될 수 μžˆλŠ” νƒ€μž…μ΄λΌλ©΄ 방어적 볡사본을 λ§Œλ“€ λ•Œ clone() 을 μ‚¬μš©ν•΄μ„œλŠ” μ•ˆλœλ‹€.




Β 

Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
p.end().setYear(78)

ν•„λ“œμ˜ λ°˜ν™˜μ—λ„ 방어적 볡사본을 λ°˜ν™˜ ν•œλ‹€.


Β 



Β 


public Date start() {
  return new Date(this.start.getTime());
}

public Date end() {
  return new Date(this.end.getTime());
}

TIP

ν΄λž˜μŠ€κ°€ ν΄λΌμ΄μ–ΈνŠΈλ‘œ 받은 ν˜Ήμ€ ν΄λΌμ΄μ–ΈνŠΈλ‘œ λ°˜ν™˜ν•˜λŠ” μš”μ†Œκ°€ κ΅¬μ„±μš”μ†Œκ°€ 가변이라면 κ·Έ μš”μ†ŒλŠ” λ°˜λ“œμ‹œ λ°©μ–΄μ μœΌλ‘œ 볡사 (Defensive Copy) ν•΄μ„œ μ‚¬μš© ν•΄μ•Ό ν•œλ‹€.
단 방어적 볡사λ₯Ό μˆ˜ν–‰ν•˜λŠ”λ° λ§Žμ€ λΉ„μš©μ΄ μ†Œμš”κ°€ λœλ‹€λ©΄ ν•΄λ‹Ή κ΅¬μ„±μš”μ†Œλ₯Ό μˆ˜μ •ν–ˆμ„λ•Œ 그에 λŒ€ν•œ μ±…μž„μ€ ν΄λΌμ΄μ–ΈνŠΈμ— μžˆμŒμ„ λ¬Έμ„œμ— λͺ…μ‹œν•˜λ„λ‘ ν•˜μž

Item 51 λ©”μ„œλ“œ μ‹œκ·Έλ‹ˆμ²˜λ₯Ό μ‹ μ€‘νžˆ μ„€κ³„ν•˜λΌ

  • λ©”μ„œλ“œ 이름을 μ‹ μ€‘νžˆ μ§“μž
    • 같은 νŒ¨ν‚€μ§€μ— μ†ν•œ λ‹€λ₯Έ 이름듀과 μΌκ΄€λ˜κ²Œ μ§“λŠ”λ‹€.
  • 편의 λ©”μ„œλ“œλ₯Ό 많이 λ§Œλ“€μ§€ 말자
    • ν΄λž˜μŠ€λ‚˜ μΈν„°νŽ˜μ΄μŠ€λŠ” μžμ‹ μ˜ 각 κΈ°λŠ₯을 μ™„λ²½νžˆ μˆ˜ν–‰ν•˜λŠ” λ©”μ„œλ“œλ‘œ μ œκ³΅ν•΄μ•Ό ν•œλ‹€.
  • λ§€κ°œλ³€μˆ˜ λͺ©λ‘μ„ 짧게 μœ μ§€ν•˜μž
    • λ§€κ°œλ³€μˆ˜ κ°―μˆ˜λŠ” 4개 μ΄ν•˜λ‘œ μœ μ§€ν•˜μž
    • 같은 νƒ€μž…μ˜ λ§€κ°œλ³€μˆ˜κ°€ μ—¬λŸ¬κ°œ 연달아 λ‚˜μ˜€λŠ” κ²½μš°λŠ” ν•΄λ‘­λ‹€.

Item 52 λ‹€μ€‘μ •μ˜λŠ” μ‹ μ€‘νžˆ μ‚¬μš©ν•˜λΌ

λ‹€μ€‘μ •μ˜ (Overloading : μ˜€λ²„λ‘œλ”©) 은 μ–΄λ–€ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν• μ§€λŠ” 컴파일 νƒ€μž„μ— κ²°μ • (Static Dispatch : 정적 λ””μŠ€νŒ¨μΉ˜) λœλ‹€.

μž¬μ •μ˜ (Overriding : μ˜€λ²„λΌμ΄λ”©) 은 λ™μ μœΌλ‘œ κ²°μ • (Dynamic Dispatch : 동적 λ””μŠ€νŒ¨μΉ˜) λœλ‹€.

λ§€κ°œλ³€μˆ˜κ°€ 같을 λ•ŒλŠ” λ‹€μ€‘μ •μ˜λ₯Ό ν”Όν•˜λŠ”κ²ƒμ΄ μ’‹λ‹€.
κΈ°μ‘΄ 클래슀λ₯Ό μˆ˜μ •ν•˜μ—¬ μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•  λ•ŒλŠ” 같은 객체λ₯Ό μž…λ ₯ λ°›λŠ” 닀쀑 μ •μ˜ λ©”μ„œλ“œλ“€μ΄ λͺ¨λ‘ λ™μΌν•˜κ²Œ λ™μž‘λ˜μ–΄μ•Ό ν•œλ‹€.

Item 53 κ°€λ³€μΈμˆ˜λŠ” μ‹ μ€‘νžˆ μ‚¬μš©ν•˜λΌ

합을 κ΅¬ν•˜λŠ” λ©”μ„œλ“œ

static int sum (int... args) {
  int sum = 0;
  for (int arg : args) {
    sum += arg;
  }

  return sum;
}
static int min (int... args) {
  if (args.length === 0) {
    throw new IllegalArgumentException("μΈμˆ˜κ°€ 1개 이상 ν•„μš”ν•©λ‹ˆλ‹€.");
  }
  
  int min = args[0];

  for (int i = 1; i < args.length; i++) {
    if (args[i] < min) {
      min = args[i];
    }
  }

  return min;
}

μœ„ μ½”λ“œλŠ” 인수λ₯Ό 0개둜 λ„˜κΈ°λ©΄ λŸ°νƒ€μž„ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€. (μ½”λ“œλ„ λ„ˆμ €λΆ„ ν•˜λ‹€.)

λ•Œλ¬Έμ— μ•„λž˜μ™€ 같이 λ¦¬νŽ ν† λ§μ΄ ν•„μš”ν•˜λ‹€.

static int min (int firstArg, int... remainingArgs) {
  int min = firstArg;
  
  for (int arg : remainingArgs) {
    if (arg < min) {
      min = arg;
    }
  }

  return min;
}

인수의 κ°œμˆ˜κ°€ μΌμ •ν•˜μ§€ μ•Šμ„λ•Œμ—λ„ λ©”μ„œλ“œλ₯Ό μ •μ˜ν•΄μ•Ό ν•œλ‹€λ©΄ κ°€λ³€μΈμˆ˜κ°€ λ°˜λ“œμ‹œ ν•„μš”ν•˜λ‹€.

λ©”μ„œλ“œλ₯Ό μ •μ˜ν• λ•ŒλŠ” ν•„μˆ˜ λ§€κ°œλ³€μˆ˜λŠ” κ°€λ³€μΈμˆ˜ μ•žμ— 두고, κ°€λ³€μΈμˆ˜λ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” μ„±λŠ₯λ¬Έμ œκΉŒμ§€ κ³ λ €ν•˜μž

Item 54 null이 μ•„λ‹Œ, 빈 μ»¬λ ‰μ…˜μ΄λ‚˜ 배열을 λ°˜ν™˜ν•˜λΌ

νŠΉμ • λ©”μ„œλ“œ μ‚¬μš©μ‹œ μ»¬λ ‰μ…˜μ΄λ‚˜ λ°°μ—΄κ³Ό 같은 μ»¨ν…Œμ΄λ„ˆκ°€ λΉ„μ—ˆμ„ λ•Œ null 을 λ°˜ν™˜ ν•˜μ˜€μ„λ•Œ
이 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλŠ” 이 상황을 처리 κ°€λŠ₯ν•œ λ°©μ–΄μ½”λ“œλ₯Ό 항상 λ„£μ–΄μ€˜μ•Ό ν•œλ‹€.

λ•Œλ¬Έμ— 빈 μ»¨ν…Œμ΄λ„ˆλ₯Ό λ°˜ν™˜ν•˜λŠ” μͺ½μ΄ 더 μ•ˆμ „ν•˜λ‹€.

public List<Cheese> getCheeses() {
  return cheesesInStock.isEmpty() ? Collections.emptyList() : new ArrayList<>(cheesesInStock);
}
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

public Cheese[] getCheeses() {
  return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}

Item 55 μ˜΅μ…”λ„ λ°˜ν™˜μ€ μ‹ μ€‘νžˆ ν•˜λΌ

Java SE 8 μ—μ„œ μΆ”κ°€λœ Optional<T> λŠ” μ›μ†Œλ₯Ό μ΅œλŒ€ 1개λ₯Ό κ°€μ§ˆ 수 μžˆλŠ” λΆˆλ³€ μ»¬λž™μ…˜μ΄λ‹€.

μ˜΅μ…”λ„μ„ λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œμ—μ„œλŠ” λ°˜λ“œμ‹œ null 을 λ°˜ν™˜ν•˜μ§€ 말자.

λ‹€μŒμ€ μ»¬λ ‰μ…˜μ—μ„œ μ΅œλŒ“κ°’μ„ ꡬ해 Optional<E> 둜 λ°˜ν™˜ν•˜λŠ” μ½”λ“œμ΄λ‹€.

public static <E extends Comparable<E>> Optional<E> max(Collection<E) c) {
  if (c.isEmpty()) {
    return Optional.empty();
  }

  E result = null;

  for (E e : c) {
    if (result = null || e.compareTo(result) > 0) {
      result = Objects.requireNonNull(e);
    }

    return Optional.of(result);
  }
}

μ˜΅μ…”λ„μ„ λ°›λŠ” ν΄λΌμ΄μ–ΈνŠΈλŠ” 그에 λ§žλŠ” μ •μ±…λ“€ (κΈ°λ³Έκ°’ ν˜Ήμ€ μ˜ˆμ™Έ λ“±λ“±) 을 μ„Έμ›Œλ‘κ³  ν•΄λ‹Ή 정책에 맞게 값을 μ„€μ •ν•œλ‹€.

λ°•μ‹±λœ κΈ°λ³Έ νƒ€μž…μ„ 담은 μ˜΅μ…”λ„μ„ λ°˜ν™˜ν•˜λŠ” 일은 없도둝 ν•˜μž (μ•„λž˜μ™€ 같은 λ°•μ‹±λœ μ˜΅μ…”λ„ νƒ€μž…μ„ μ‚¬μš©)

  • OptionalInt
  • OptionalLong
  • OptionalDouble

Item 56 곡개된 API μš”μ†Œμ—λŠ” 항상 λ¬Έμ„œν™” 주석을 μž‘μ„±ν•˜λΌ

μž‘μ„±ν•œ API λ₯Ό μ˜¬λ°”λ₯΄κ²Œ λ¬Έμ„œν™” ν•˜λ €λ©΄ 곡개된 λͺ¨λ“  클래슀, μΈν„°νŽ˜μ΄μŠ€, λ©”μ„œλ“œ ν•„λ“œ 선언에 λ¬Έμ„œν™” 주석을 달아야 ν•œλ‹€.

λ¬Έμ„œν™” 주석이 μ—†λ‹€λ©΄ API κ°€ μ“°κΈ° μ–΄λ €μš°λ©° 였λ₯˜λ₯Ό λ‚΄κΈ° 쉽닀.

μ „μ œμ‘°κ±΄ 및 사후쑰건, 였λ₯˜μ‚¬ν•­ 같은 λΆ€μž‘μš© λ˜ν•œ λ¬Έμ„œν™” ν•΄μ•Ό ν•œλ‹€.