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

μ•„μ΄ν…œ 28μ—μ„œ λ‚˜μ˜¨ 것 처럼 λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ€ λΆˆκ³΅λ³€μ΄λ‹€(List<String>은 List<Object>의 ν•˜μœ„νƒ€μž…μ΄ μ•„λ‹Œ κ²ƒμ²˜λŸΌ)

ν•˜μ§€λ§Œ μ™€μΌλ“œμΉ΄λ“œμ²˜λŸΌ λΆˆκ³΅λ³€ 방식보닀 μœ μ—°ν•œ 무언가가 ν•„μš”ν•˜λ‹€.

μ™€μΌλ“œμΉ΄λ“œ

μ•„λž˜ μ½”λ“œλŠ” μ•„μ΄ν…œ 29의 Stack 클래슀의 public API만 μΆ”λ¦° 것에 pushAll λ©”μ„œλ“œλ₯Ό μΆ”κ°€ν•œ 것이닀.

public class Stack<E> {
	public Stack();
	public void push(E e);
	public E pop();
	public boolean isEmpty();
	
	// μ™€μΌλ“œ μΉ΄λ“œλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ€ pushAll 예제
	// μž…λ ₯ λ§€κ°œλ³€μˆ˜ μ›μ†Œλ₯Ό μŠ€νƒμ— λ„£λŠ” λ©”μ„œλ“œ
	public void pushAll(Iterable<E> src) {
		for (E e : src)
			push(e);
		}
	}
public class Test{
	public static void main(String[] args) {
		Stack<Number> numberStack = new Stack<>();
		Iterable<Integer> integers = Arrays.asList(
			Integer.valueOf(1), Integer.valueOf(2));
		numberStack.pushAll(integers);
	}
}

μœ„ μ½”λ“œμ²˜λŸΌ Stackλ₯Ό μ„ μ–Έν•œ ν›„ pushAll에 Iterable νƒ€μž…μ„ λ„£μœΌλ©΄ 잘 μž‘λ™ν•  것이라 κΈ°λŒ€ν•  수 μžˆμœΌλ‚˜, λΆˆκ³΅λ³€ νƒ€μž…μ΄κΈ° λ•Œλ¬Έμ— 였λ₯˜κ°€ λ°œμƒν•œλ‹€.

μƒμ‚°μž μ™€μΌλ“œμΉ΄λ“œ

μœ„ μ½”λ“œλ₯Ό μ˜¬λ°”λ₯΄κ²Œ μž‘λ™ν•˜λŠ” 방법은 μƒμ‚°μž μ™€μΌλ“œμΉ΄λ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 것이닀. μƒμ‚°μž μ™€μΌλ“œμΉ΄λ“œλŠ” ν•˜μœ„ νƒ€μž…μ„ κ°•μ œν•˜κ³ μž ν•  λ•Œ 쓰인닀.

public void pushAll(Iterable<? extends E> src) {
    for (E e : src) {
        push(e);
    }
}
  • Iterable<? extends E> src : pushAll의 μž…λ ₯ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ€ 'E의 Iterable'이 μ•„λ‹ˆλΌ 'E의 ν•˜μœ„ νƒ€μž…μ˜ Iterable'이어야 ν•œλ‹€λŠ” 것을 λ‚˜νƒ€λ‚΄λŠ” μ™€μΌλ“œμΉ΄λ“œ.

μ†ŒλΉ„μž μ™€μΌλ“œμΉ΄λ“œ

μ†Œμ‹œλ°” μ™€μΌλ“œμΉ΄λ“œλŠ” μƒμ‚°μž μ™€μΌλ“œμΉ΄λ“œμ™€ λ°˜λŒ€μ˜ κ°œλ…μœΌλ‘œ μƒμœ„ νƒ€μž…μ„ κ°•μ œν•˜κ³ μž ν•  λ•Œ 쓰인닀.

예λ₯Όλ“€μ–΄, μ•„λž˜ popAll λ©”μ„œλ“œλŠ” λ§€κ°œλ³€μˆ˜μ˜ Collection에 Stack이 κ°€μ§„ μ›μ†Œλ₯Ό λ„˜κΈΈ λ•Œ μ“°μ΄λŠ” λ©”μ„œλ“œμ΄λ‹€. λ©”μ„œλ“œμ˜ μ˜λ„λΌλ©΄ μ›μ†Œκ°€ μƒμ˜ μ›μ†ŒμΈ Collectionμ—κ²Œ popAll이 정상 μž‘λ™ν•΄μ•Όν•˜κ³  이럴 λ•Œ μ†ŒλΉ„μž μ™€μΌλ“œμΉ΄λ“œλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

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

μ™€μΌλ“œμΉ΄λ“œ 적용 곡식

μ™€μΌλ“œμΉ΄λ“œλ₯Ό μ μš©ν•˜λŠ”λ° μ“Έ 수 μžˆλŠ” 곡식이 μžˆλ‹€. λ§€κ°œλ³€μˆ˜ν™” νƒ€μž… Tκ°€ μƒμ‚°μžλΌλ©΄ <? extends T>λ₯Ό μ‚¬μš©ν•˜κ³ , μ†ŒλΉ„μžλΌλ©΄ <? super T>λ₯Ό μ‚¬μš©ν•˜λŠ” 것이닀.

Tκ°€ μƒμ‚°μžλΌλ©΄, λ‹€λ₯Έ κ°μ²΄λ‘œλΆ€ν„° 전달 λ°›μ•„μ•Όν•˜κΈ°μ—, λ‚΄ μžμ‹ λ³΄λ‹€ ν•΄λ‹Ή κ°μ²΄λŠ” ν•˜μœ„ μ›μ†Œμ΄μ—¬μ•Ό ν•œλ‹€.

Tκ°€ μ†ŒλΉ„μžλΌλ©΄, λ‹€λ₯Έ 객체둜 전달 ν•΄μ•Όν•˜κΈ°μ—, λ‚΄ μžμ‹ λ³΄λ‹€ ν•΄λ‹Ή κ°μ²΄λŠ” μƒμœ„ μ›μ†Œμ΄μ—¬μ•Ό ν•œλ‹€.

λ”°λΌμ„œ μ•„λž˜μ™€ 같은 곡식이 성립할 수 μžˆλ‹€.

νŽ™μŠ€(PECS): producer-extends, consumer-super

λ°˜ν™˜νƒ€μž…μ˜ μ™€μΌλ“œμΉ΄λ“œ 적용

λ°˜ν™˜ νƒ€μž…μ—λŠ” ν•œμ •μ  μ™€μΌλ“œμΉ΄λ“œ νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ μ•ˆλœλ‹€. 예λ₯Ό λ“€μ–΄ set 2개λ₯Ό ν•©μΉ˜λŠ” union λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•  경우, PECS 원칙에 따라 μƒμ‚°μž λ©”μ„œλ“œμ— ν•΄λ‹Ήλ˜λ©° extendλ₯Ό μ μš©ν•œλ‹€. ν•˜μ§€λ§Œ λ°˜ν™˜νƒ€μž…μ—λ„ μ™€μΌλ“œμΉ΄λ“œλ₯Ό μ μš©ν•  경우 ν΄λΌμ΄μ–ΈνŠΈμ—μ„œλ„ μ™€μΌλ“œμΉ΄λ“œ νƒ€μž…μ„ μ¨μ•Όν•œλ‹€.

μ•„λž˜ μ½”λ“œκ°€ λ°˜ν™˜νƒ€μž…μ˜ μ™€μΌλ“œμΉ΄λ“œ λ―Έ μ μš©μ‹œ 이점을 보여쀀닀. 클래슀 μ‚¬μš©μžλŠ” μ™€μΌλ“œμΉ΄λ“œ νƒ€μž…μ΄ μ“°μ˜€λ‹€λŠ” 사싀쑰차 μ˜μ‹ν•˜μ§€ λͺ»ν•˜κ³  μ‚¬μš©ν•  수 μžˆλŠ” μ½”λ“œκ°€ λœλ‹€.

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

	public static void main(String[] args) {
		// java 8 이상
		Set<Double> doubleSet = Set.of(1.0, 2.1);
		Set<Integer> integerSet = Set.of(1, 2);
		Set<Number> unionSet = union(doubleSet, integerSet);

		/*
		java 7 μ΄ν•˜
		μ»΄νŒŒμΌλŸ¬κ°€ μ˜¬λ°”λ₯Έ 탕비을 μΆ”λ‘ ν•˜μ§€ λͺ»ν•΄
		λͺ…μ‹œμ  νƒ€μž… 인수λ₯Ό μ‚¬μš©ν•΄ νƒ€μž…μ„ μ•Œλ €μ£Όλ©΄ λœλ‹€.
		Set<Double> doubleSet = new HashSet<>(Set.of(1.0, 2.1));
		Set<Integer> integerSet = new HashSet<>(Set.of(1, 2));
		Set<Number> unionSet = Union.<Number>union(doubleSet, integerSet);
		*/
	}
}

Comparator와 Comparable

Comparable<E> λ³΄λ‹€λŠ” Comparable<? super E>λ₯Ό μ‚¬μš©ν•˜κ³ , Compator<E> λ³΄λ‹€λŠ” Comparator<? super E>λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

public interface Comparable<E>
public interface Delayed extends Comparable<Delayed>
public interface ScheduledFuture<V> extends Delayed, Future<V>

μœ„ μ½”λ“œλŠ” Comparable, Delayed, ScheduledFuture μΈν„°νŽ˜μ΄μŠ€μ˜ 선언뢀이닀. νŠΉμ§•μ€, ScheduledFuture μΈν„°νŽ˜μ΄μŠ€λŠ” Comparable을 extendsν•˜μ§€ μ•Šκ³  Delayed만 extends ν•˜μ˜€λ‹€.

λ”°λΌμ„œ, Comparableλ₯Ό μ‚¬μš©ν•  경우, Delayed μΈμŠ€ν„΄μŠ€μ™€ ScheduledFuture 비ꡐ κ°€λŠ₯μ„± λ•Œλ¬Έμ— 정상 μž‘λ™ν•˜μ§€ μ•Šκ²Œ λœλ‹€.

λ©”μ„œλ“œ 선언에 νƒ€μž… λ§€κ°œλ³€μˆ˜κ°€ ν•œ 번 λ‚˜μ˜€λ©΄ μ™€μΌλ“œ μΉ΄λ“œλ‘œ λŒ€μ²΄ν•˜λΌ

μ•„λž˜ 첫번째 선언은 λΉ„ν•œμ •μ  νƒ€μž… λ§€κ°œλ³€μˆ˜λ₯Ό μ‚¬μš©ν–ˆκ³  두 λ²ˆμ§ΈλŠ” λΉ„ν•œμ •μ  μ™€μΌλ“œμΉ΄λ“œλ₯Ό μ‚¬μš©ν–ˆλ‹€.

public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);

λ‘λ²ˆμ§Έ 방법을 μ‚¬μš©ν•΄μ•Ό ν•œλ‹€(λ©”μ„œλ“œ 선언에 νƒ€μž… λ§€κ°œλ³€μˆ˜κ°€ ν•œ 번만 λ‚˜μ˜€λ©΄ μ™€μΌλ“œ μΉ΄λ“œλ‘œ λŒ€μ²΄ν•˜λΌ). μ–΄λ–€ λ¦¬μŠ€νŠΈμ΄λ“  이 λ©”μ„œλ“œμ— λ„˜κΈ°λ©΄ λͺ…μ‹œν•œ 인덱슀의 μ›μ†Œλ“€μ„ κ΅ν™˜ν•΄ 쀄 것이며 μ‹ κ²½ 써야 ν•  νƒ€μž… λ§€κ°œλ³€μˆ˜λ„ μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

ν•˜μ§€λ§Œ, λ‘λ²ˆμ§Έ 방법도 단점이 μžˆλ‹€. μ•„λž˜μ™€ 같은 직관적인 μ½”λ“œλ„ μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¨λ‹€. List<?>μ—λŠ” null μ™Έμ—λŠ” μ–΄λ–€ 값도 넣을 수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

public static void swap(List<?> list, int i, int j) {
    list.set(i, list.set(j, list.get(i));
}

μ΄λ•Œ, λΉ„ν•œμ •μ  νƒ€μž… λ§€κ°œλ³€μˆ˜ 방법을 μΆ”κ°€λ‘œ μ‚¬μš©ν•˜μ—¬ μ™€μΌλ“œμΉ΄λ“œ νƒ€μž…μ˜ μ‹€μ œ νƒ€μž…μ„ μ•Œλ €μ£ΌλŠ” λ©”μ„œλ“œλ₯Ό private λ„μš°λ―Έ λ©”μ„œλ“œλ‘œ μ‚¬μš©ν•˜λŠ” 방법이닀.

public static void swap(List<?> list, int i, int j) {
    swapHelper(i, list.set(j, list.get(i));
}
public static <E> void swapHelper(List<E> list, int i, int j) {
    list.set(i, list.set(j, list.get(i));
}
⚠️ **GitHub.com Fallback** ⚠️