item 19 sijun - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

상속을 κ³ λ €ν•΄ μ„€κ³„ν•˜κ³  λ¬Έμ„œν™”ν•˜λΌ. κ·ΈλŸ¬μ§€ μ•Šμ•˜λ‹€λ©΄ 상속을 κΈˆμ§€ν•˜λΌ

μƒμ†μš© ν΄λž˜μŠ€λŠ” λ¬Έμ„œν™”ν•˜λΌ

μƒμ†μš© ν΄λž˜μŠ€λŠ” μž¬μ •μ˜ν•  수 μžˆλŠ” λ©”μ„œλ“œλ“€μ„ λ‚΄λΆ€μ μœΌλ‘œ μ–΄λ–»κ²Œ μ΄μš©ν•˜λŠ”μ§€(μžκΈ°μ‚¬μš©) λ¬Έμ„œλ‘œ 남겨야 ν•œλ‹€. μž¬μ •μ˜ κ°€λŠ₯μ΄λž€ publicκ³Ό protected λ©”μ„œλ“œ 쀑 final이 μ•„λ‹Œ λͺ¨λ“  λ©”μ„œλ“œλ₯Ό μ˜λ―Έν•œλ‹€.

λ¬Έμ„œλ‘œ λ‚¨κΈ°λŠ” λ°©λ²•μœΌλ‘œ @implSpec 을 μ‚¬μš©ν•˜λŠ” 것이 μžˆλ‹€. μ•„λž˜μ™€ 같이 Tagλ₯Ό μ‚¬μš©ν•˜λ©΄ μžλ°”λ… 도ꡬ가 "Implementation Requirements"둜 μ‹œμž‘ν•˜λŠ” μ ˆμ„ API λ¬Έμ„œμ˜ λ©”μ„œλ“œμ— μΆ”κ°€ν•΄μ€€λ‹€.

/**
 * @implSpec
 * 이곳에 λ‚΄μš©μ„ λ„£μœΌλ©΄ "Implementation Requirements"둜 μ‹œμž‘ν•˜λŠ” 절이 μƒμ„±λœλ‹€ 
 */
public class Test {
	...
}

예λ₯Ό λ“€μ–΄, java.util.AbstractionCollection의 μžλ°”λ… λ‚΄μš©μ˜ λ²ˆμ—­μ΄λ‹€.

μ£Όμ–΄μ§„ μ›μ†Œκ°€ 이 μ»¬λ ‰μ…˜ μ•ˆμ— μžˆλ‹€λ©΄ κ·Έ μΈμŠ€ν„΄μŠ€λ₯Ό ν•˜λ‚˜ μ œκ±°ν•œλ‹€.(선택적 λ™μž‘) 더 μ •ν™•ν•˜κ²Œ λ§ν•˜λ©΄, 이 μ»¬λ ‰μ…˜ μ•ˆμ— 'Object.equals(o, e)κ°€ 참인 μ›μ†Œ' eκ°€ ν•˜λ‚˜ 이상 μžˆλ‹€λ©΄ κ·Έ 쀑 ν•˜λ‚˜λ₯Ό μ œκ±°ν•œλ‹€. μ£Όμ–΄μ§„ μ›μ†Œκ°€ μ»¬λ ‰μ…˜ μ•ˆμ— μžˆμ—ˆλ‹€λ©΄, (즉, 호좜 κ²°κ³Ό 이 μ»¬λ ‰μ…˜μ΄ 변경됐닀면) trueλ₯Ό λ°˜ν™˜ν•œλ‹€.

Implemented RequirementsΒ : 이 λ©”μ†Œλ“œλŠ” μ»¬λ ‰μ…˜μ„ μˆœνšŒν•˜λ©° μ£Όμ–΄μ§„ μ›μ†Œλ₯Ό 찾도둝 κ΅¬ν˜„λ˜μ—ˆλ‹€. μ£Όμ–΄μ§„ μ›μ†Œλ₯Ό 찾으면 반볡자의 remove λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•΄ μ»¬λ ‰μ…˜μ—μ„œ μ œκ±°ν•œλ‹€. 이 μ»¬λ ‰μ…˜μ΄ μ£Όμ–΄μ§„ 객체λ₯Ό κ°–κ³  μžˆμœΌλ‚˜, 이 μ»¬λ ‰μ…˜μ˜ iterator λ©”μ†Œλ“œκ°€ λ°˜ν™˜ν•œ λ°˜λ³΅μžκ°€ remove λ©”μ†Œλ“œλ₯Ό κ΅¬ν˜„ν•˜μ§€ μ•Šμ•˜λ‹€λ©΄ UnsupportedOperationException을 λ˜μ§€λ‹ˆ μ£Όμ˜ν•˜μž.

μ„€λͺ…에 λ”°λ₯΄λ©΄ iterator λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜ν•˜λ©΄ remove λ©”μ„œλ“œμ˜ λ™μž‘μ— 영ν–₯을 μ€Œμ„ ν™•μ‹€νžˆ μ•Œ 수 μžˆλ‹€.

클래슀의 λ‚΄λΆ€ λ™μž‘ κ³Όμ • 쀑간에 끼어듀 수 μžˆλŠ” ν›…(hook)을 잘 μ„ λ³„ν•˜μ—¬ protected λ©”μ„œλ“œ ν˜•νƒœλ‘œ κ³΅κ°œν•΄μ•Ό ν•  μˆ˜λ„ μžˆλ‹€

protected void removeRange(int fromIndex, int toIndex)

fromIndex(포함)λΆ€ν„° toIndex(미포함)κΉŒμ§€μ˜ λͺ¨λ“  μ›μ†Œλ₯Ό 이 λ¦¬μŠ€νŠΈμ—μ„œ μ œκ±°ν•œλ‹€. toIndex μ΄ν›„μ˜ μ›μ†Œλ“€μ€ μ•žμœΌλ‘œ 당겨진닀. 이 호좜둜 λ¦¬μŠ€νŠΈλŠ” 'toIndex - fromIndex'만큼 μ§§μ•„μ§„λ‹€.

이 리슀트 ν˜Ήμ€ 이 리슀트의 λΆ€λΆ„λ¦¬μŠ€νŠΈμ— μ •μ˜λœ clear 연산이 이 λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€. 리슀트 κ΅¬ν˜„μ˜ λ‚΄λΆ€ ꡬ쑰λ₯Ό ν™œμš©ν•˜λ„λ‘ 이 λ©”μ†Œλ“œλ₯Ό μž¬μ •μ˜ν•˜λ©΄ 이 λ¦¬μŠ€νŠΈμ™€ λΆ€λΆ„λ¦¬μŠ€νŠΈμ˜ clear μ—°μ‚° μ„±λŠ₯을 크게 κ°œμ„ ν•  수 μžˆλ‹€.

Implemented RequirementsΒ : 이 λ©”μ†Œλ“œλŠ” fromIndexμ—μ„œ μ‹œμž‘ν•˜λŠ” 리슀트 반볡자λ₯Ό μ–»μ–΄ λͺ¨λ“  μ›μ†Œλ₯Ό μ œκ±°ν•  λ•ŒκΉŒμ§€ ListIterator.next와 ListIterator.removeλ₯Ό 반볡 ν˜ΈμΆœν•œλ‹€.

주의: ListIterator.removeκ°€ μ„ ν˜• μ‹œκ°„μ΄ 걸리면 이 κ΅¬ν˜„μ˜ μ„±λŠ₯은 μ œκ³±μ— λΉ„λ‘€ν•œλ‹€.

List κ΅¬ν˜„μ²΄μ˜ μ΅œμ’… μ‚¬μš©μžλŠ” removeRange λ©”μ†Œλ“œμ— 관심이 μ—†λ‹€. κ·ΈλŸΌμ—λ„ 이 λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œ μ΄μœ λŠ” 단지 ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ λΆ€λΆ„λ¦¬μŠ€νŠΈμ˜ clear λ©”μ„œλ“œλ₯Ό κ³ μ„±λŠ₯으둜 λ§Œλ“€κΈ° μ‰½κ²Œ ν•˜κΈ° μœ„ν•΄μ„œλ‹€.

μƒμ†μš© 클래슀의 μƒμ„±μžλŠ” μž¬μ •μ˜ κ°€λŠ₯ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄μ„œλŠ” μ•ˆλœλ‹€

μƒμœ„ 클래슀의 μƒμ„±μžκ°€ ν•˜μœ„ 클래슀의 μƒμ„±μžλ³΄λ‹€ λ¨Όμ € μ‹€ν–‰λ˜λ―€λ‘œ ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ μž¬μ •μ˜ν•œ λ©”μ„œλ“œκ°€ ν•˜μœ„ 클래슀의 μƒμ„±μžλ³΄λ‹€ λ¨Όμ € ν˜ΈμΆœλœλ‹€. μ΄λ•Œ κ·Έ μž¬μ •μ˜ν•œ λ©”μ„œλ“œκ°€ ν•˜μœ„ 클래슀의 μƒμ„±μžμ—μ„œ μ΄ˆκΈ°ν™”ν•˜λŠ” 값에 μ˜μ‘΄ν•œλ‹€λ©΄ μ˜λ„λŒ€λ‘œ λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€.

public class Super {
	// 잘λͺ»λœ 예 - μƒμ„±μžκ°€ μž¬μ •μ˜ κ°€λŠ₯ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€.
	public Super(){
		overrideMe();
	}
	
	public void overrideMe() {
	}
}
public final class Sub extends Super {
	// μ΄ˆκΈ°ν™”λ˜μ§€ μ•Šμ€ final ν•„λ“œ, μƒμ„±μžμ—μ„œ μ΄ˆκΈ°ν™”ν•œλ‹€.
	private final Instant instant;
	
	Sub() {
		instant = Instant.now();
	}

	// μž¬μ •μ˜ κ°€λŠ₯ λ©”μ„œλ“œ. μƒμœ„ 클래슀의 μƒμ„±μžκ°€ ν˜ΈμΆœν•œλ‹€.
	@Override public void overrideMe() {
		System.out.println(instant);
	}

	public static void main(String[] args) {
		Sub sub = new Sub();
		sub.overrideMe();
	}
}

이 ν”„λ‘œκ·Έλž¨μ΄ instantλ₯Ό 두 번 좜λ ₯ν•˜λ¦¬λΌ κΈ°λŒ€ν–ˆκ² μ§€λ§Œ, 첫 λ²ˆμ§ΈλŠ” null을 좜λ ₯ν•œλ‹€. μƒμœ„ 클래슀의 μƒμ„±μžλŠ” ν•˜μœ„ 클래슀의 μƒμ„±μžκ°€ μΈμŠ€ν„΄μŠ€ ν•„λ“œλ₯Ό μ΄ˆκΈ°ν™”ν•˜κΈ°λ„ 전에 overrideMeλ₯Ό ν˜ΈμΆœν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

cloneκ³Ό readObject λ©”μ„œλ“œλŠ” μƒμ„±μžμ™€ λΉ„μŠ·ν•œ 효과λ₯Ό λ‚Έλ‹€. λ”°λΌμ„œ μƒμ†μš© ν΄λž˜μŠ€μ—μ„œ Cloneableμ΄λ‚˜ Serializable을 κ΅¬ν˜„ν• μ§€ μ •ν•΄μ•Ό ν•œλ‹€λ©΄, 이듀을 κ΅¬ν˜„ν•  λ•Œ λ”°λ₯΄λŠ” μ œμ•½λ„ μƒμ„±μžμ™€ λΉ„μŠ·ν•˜λ‹€λŠ” 점에 μ£Όμ˜ν•˜μž. 즉, cloneκ³Ό readObject λͺ¨λ‘ μ§μ ‘μ μœΌλ‘œλ“  κ°„μ ‘μ μœΌλ‘œλ“  μž¬μ •μ˜ κ°€λŠ₯ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄μ„œλŠ” μ•ˆ λœλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ, Serializable을 κ΅¬ν˜„ν•œ μƒμ†μš© ν΄λž˜μŠ€κ°€ readResolveλ‚˜ writeReplace λ©”μ„œλ“œλ₯Ό κ°–λŠ”λ‹€λ©΄ 이 λ©”μ„œλ“œλ“€μ€ private이 μ•„λ‹Œ protected둜 μ„ μ–Έν•΄μ•Ό ν•œλ‹€. private으둜 μ„ μ–Έν•œλ‹€λ©΄ ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ λ¬΄μ‹œλ˜κΈ° λ•Œλ¬Έμ΄λ‹€. 이 μ—­μ‹œ 상속을 ν—ˆμš©ν•˜κΈ° μœ„ν•΄ λ‚΄λΆ€ κ΅¬ν˜„μ„ 클래슀 API둜 κ³΅κ°œν•˜λŠ” 예 쀑 ν•˜λ‚˜λ‹€.

μƒμ†μš©μœΌλ‘œ μ„€κ³„ν•˜μ§€ μ•Šμ€ ν΄λž˜μŠ€λŠ” 상속을 κΈˆμ§€ν•˜λΌ

방법은 2κ°€μ§€ 이닀.

  1. 클래슀λ₯Ό final둜 μ„ μ–Έν•˜λŠ” 방법
  2. λͺ¨λ“  μƒμ„±μžλ₯Ό privateμ΄λ‚˜ package-private으둜 γ……λ„λ„ˆν•˜κ³  public 정적 νŒ©ν„°λ¦¬λ₯Ό λ§Œλ“€μ–΄ μ£ΌλŠ” 방법

핡심 정리

μƒμ†μš© 클래슀λ₯Ό 섀계 ν•˜κΈ°λž€ κ²°μ½” 만만치 μ•Šλ‹€. 클래슀 λ‚΄λΆ€μ—μ„œ 슀슀둜λ₯Ό μ–΄λ–»κ²Œ μ‚¬μš©ν•˜λŠ”μ§€(μžκΈ°μ‚¬μš© νŒ¨ν„΄) λͺ¨λ‘ λ¬Έμ„œλ‘œ 남겨야 ν•˜λ©°, 일단 λ¬Έμ„œν™”ν•œ 것은 κ·Έ ν΄λž˜μŠ€κ°€ μ“°μ΄λŠ” ν•œ λ°˜λ“œμ‹œ μ§€μΌœμ•Ό ν•œλ‹€. κ·ΈλŸ¬μ§€ μ•ŠμœΌλ©΄ κ·Έ λ‚΄λΆ€ κ΅¬ν˜„ 방식을 λ―Ώκ³  ν™œμš©ν•˜λ˜ ν•˜μœ„ 클래슀λ₯Ό μ˜€λ™μž‘ν•˜κ²Œ λ§Œλ“€ 수 μžˆλ‹€. λ‹€λ₯Έ 이가 효율 쒋은 ν•˜μœ„ 클래슀λ₯Ό λ§Œλ“€ 수 μžˆλ„λ‘ 일뢀 λ©”μ„œλ“œλ₯Ό protected둜 μ œκ³΅ν•΄μ•Ό ν•  μˆ˜λ„ μžˆλ‹€. κ·ΈλŸ¬λ‹ˆ 클래슀λ₯Ό ν™•μž₯ν•΄μ•Ό ν•  λͺ…ν™•ν•œ μ΄μœ κ°€ λ– μ˜€λ₯΄μ§€ μ•ŠμœΌλ©΄ 상속을 κΈˆμ§€ν•˜λŠ” 편이 λ‚˜μ„ 것이닀. 상속을 κΈˆμ§€ν•˜λ €λ©΄ 클래슀λ₯Ό final둜 μ„ μ–Έν•˜κ±°λ‚˜ μƒμ„±μž λͺ¨λ‘λ₯Ό μ™ΈλΆ€μ—μ„œ μ ‘κ·Όν•  수 없도둝 λ§Œλ“€λ©΄ λœλ‹€.