Java/๐Ÿ“• Effective Java

[Effective Java] ๋ชจ๋“  ๊ฐ์ฒด์˜ ๊ณตํ†ต ๋ฉ”์„œ๋“œ ไธ‹

soogoori 2024. 9. 22. 18:38

13. clone ์žฌ์ •์˜๋Š” ์ฃผ์˜ํ•ด์„œ ์ง„ํ–‰

  • Cloneable : ๋ณต์ œํ•ด๋„ ๋˜๋Š” ํด๋ž˜์Šค์ž„์„ ๋ช…์‹œํ•˜๋Š” ์šฉ๋„์˜ ์ธํ„ฐํŽ˜์ด์Šค
    • Object์˜ protected ๋ฉ”์„œ๋“œ์ธ clone์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ๊ฒฐ์ • 
    • Cloneable์€ ์ƒ์œ„ ํด๋ž˜์Šค์— ์ •์˜๋œ protected ๋ฉ”์„œ๋“œ์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ๋ณ€๊ฒฝ
    • ์ƒ์† ๊ด€๊ณ„์˜ ๋‘ ํด๋ž˜์Šค ์ค‘ ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ super.clone์„ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด ์ž˜๋ชป๋œ ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์งˆ ์ˆ˜ ์žˆ์Œ
      ๐Ÿ‘‰ ํด๋ž˜์Šค B๊ฐ€ ํด๋ž˜์Šค A๋ฅผ ์ƒ์†ํ•  ๋•Œ, ํ•˜์œ„ ํด๋ž˜์Šค์ธ B์˜ clone์€ B ํƒ€์ž… ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•จ. ํ•˜์ง€๋งŒ A์˜ clone์ด new A(...)๋กœ ์ƒ์„ฑํ•œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋ฉด, B์˜ clone๋„ A ํƒ€์ž… ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜.

โœณ๏ธ ๊ฐ€๋ณ€ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๋Š” 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_INITIAL_CAPACITY];
    }
    ...
}
  • clone ๋ฉ”์„œ๋“œ๊ฐ€ ๋‹จ์ˆœํžˆ super.clone์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋ฉด elements ํ•„๋“œ๋Š” ์›๋ณธ Stack ์ธ์Šคํ„ด์Šค์™€ ๋˜‘๊ฐ™์€ ๋ฐฐ์—ด์„ ์ฐธ์กฐํ•˜๊ฒŒ๋จ. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์›๋ณธ์ด๋‚˜ ๋ณต์ œ๋ณธ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ๋‹ค๋ฅธ ํ•˜๋‚˜๋„ ์ˆ˜์ •๋˜์–ด ๋ถˆ๋ณ€์‹์„ ํ•ด์นจ.
  • clone ๋ฉ”์„œ๋“œ๊ฐ€ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋ ค๋ฉด ์Šคํƒ ๋‚ด๋ถ€ ์ •๋ณด๋ฅผ ๋ณต์‚ฌํ•ด์•ผํ•จ ๐Ÿ‘‰ elements ๋ฐฐ์—ด์˜ clone์„ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœ
@Override
public Stack clone(){
	try{
    	Stack result = (Stack) super.clone();
        result.elements = elements.clone();
        return result;
    } catch(CloneNotSupportedException e){
    	throw new AssertionError();
    }
}

 

 

โš ๏ธ ์•„๋ž˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์žฌ๊ท€์  ํ˜ธ์ถœ์ด ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์„ ๋•Œ๋„ ์žˆ์Œ 

public class HashTable implements Cloneable {
  private Entry[] buckets = ...;

  private static class Entry {
    final Object key;
    Object value;
    Entry next;

    Entry(Obejct key, Object value, Entry next) {
      this.key = key;
      this.value = value;
      this.next = next;
    }
  }
  ...//์ƒ๋žต
}
 @Override 
    public HashTable clone() {
    try {
      HashTable result = (HashTable) super.clone();
      result.buckets = buckets.clone();
      return result;
    } catch (CloneNotSupportedException e) {
      throw new AssertionError();
    }
  }
  • ๋ณต์ œ๋ณธ์€ ์ž์‹ ๋งŒ์˜ ๋ฒ„ํ‚ท ๋ฐฐ์—ด์„ ๊ฐ–์ง€๋งŒ, ์ด ๋ฐฐ์—ด์€ ์›๋ณธ๊ณผ ๊ฐ™์€ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋ฅผ ์ฐธ์กฐํ•จ
  • ๊ฐ ๋ฒ„ํ‚ท์„ ๊ตฌ์„ฑํ•˜๋Š” ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณต์‚ฌํ•จ์œผ๋กœ์จ ๋ฌธ์ œ ํ•ด๊ฒฐ 
public class HashTable implements Cloneable {
  private Entry[] buckets = ...;

  private static class Entry {
    final Object key;
    Object value;
    Entry next;

    Entry(Obejct key, Object value, Entry next) {
      this.key = key;
      this.value = value;
      this.next = next;
    }

    //์ด ์—”ํŠธ๋ฆฌ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ๋ณต์‚ฌ
    Entry deepCopy() {
      return new Entry(key, value, next == null ? null : next.deepCopy());
    }
  }

  @Override 
  public HashTable clone() {
    try {
      HashTable result = (HashTable) super.clone();
      result.buckets = new Entry[buckets.length]; // 1. ์ ์ ˆํ•œ ํฌ๊ธฐ์˜ ์ƒˆ๋กœ์šด ๋ฒ„ํ‚ท ๋ฐฐ์—ด ํ• ๋‹น
      for (int i = 0; i < buckets.length; i++) { // 2. ์›๋ž˜์˜ ๋ฒ„ํ‚ท ๋ฐฐ์—ด์„ ์ˆœํšŒํ•˜๋ฉฐ 
        if (buckets[i] != null) { // ๋น„์ง€ ์•Š์€ ๊ฐ ๋ฒ„ํ‚ท์— ๋Œ€ํ•ด ๊นŠ์€ ๋ณต์‚ฌ
          result.buckets[i] = buckets[i].deepCopy();
        }
      }
      return result;
    } catch (CloneNotSupportedException e) {
      throw new AssertionError();
    }
  }
  ...
}

๐Ÿ‘† ํ•˜์ง€๋งŒ ์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ์—์„œ๋„ ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ ! ์žฌ๊ท€ ํ˜ธ์ถœ ๋•Œ๋ฌธ์— ๋ฆฌ์ŠคํŠธ์˜ ์›์†Œ ์ˆ˜๋งŒํผ ์Šคํƒ ํ”„๋ ˆ์ž„์„ ์†Œ๋น„ํ•˜๋ฏ€๋กœ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๊ธธ๋ฉด StackOverFlow ๋ฐœ์ƒํ•จ. 

๊ทธ๋Ÿฌ๋ฏ€๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด ์žฌ๊ท€ํ˜ธ์ถœ ๋Œ€์‹  ๋ฐ˜๋ณต์ž๋ฅผ ์จ์„œ ์ˆœํšŒํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ˆ˜์ •ํ•ด์•ผ ํ•จ.

// ์—”ํŠธ๋ฆฌ ์ž์‹ ์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ณต์‚ฌ
Entry deepCopy() {
  Entry result = new Entry(key, value, next);
  for (Entry p = result; p.next != null; p = p.next) {
    p.next = new Entry(p.next.key, p.next.value, p.next.next);
  }
  return result;
}

 


Cloneable์„ ์ด๋ฏธ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•œ๋‹ค๋ฉด clone์„ ์ž˜ ์ž‘๋™ํ•˜๋„๋ก ๊ตฌํ˜„ํ•ด์•ผํ•จ...
ํ•˜์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์€ ์ƒํ™ฉ์—์„œ๋Š” ๋ณต์‚ฌ ์ƒ์„ฑ์ž์™€ ๋ณต์‚ฌ ํŒฉํ† ๋ฆฌ๋กœ ๋” ๋‚˜์€ ๊ฐ์ฒด ๋ณต์‚ฌ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค ! 

โœณ๏ธ ๋ณต์‚ฌ ์ƒ์„ฑ์ž (๋ณ€ํ™˜ ์ƒ์„ฑ์ž)

 

๐Ÿ‘‰ ์ž์‹ ๊ณผ ๊ฐ™์€ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ์ƒ์„ฑ์ž 

public Yum(Yum yum) { ... };

 

 

โœณ๏ธ ๋ณต์‚ฌ ํŒฉํ† ๋ฆฌ (๋ณ€ํ™˜ ํŒฉํ† ๋ฆฌ)

 

๐Ÿ‘‰ ๋ณต์‚ฌ ์ƒ์„ฑ์ž๋ฅผ ๋ชจ๋ฐฉํ•œ ์ •์  ํŒฉํ† ๋ฆฌ

public static Yum newInstance(Yum yum) { ... };

 

๋ณต์ œ ๊ธฐ๋Šฅ์€ ์ƒ์„ฑ์ž์™€ ํŒฉํ† ๋ฆฌ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ์ตœ๊ณ ๋‹ค.. 
๋‹จ, ๋ฐฐ์—ด๋งŒ์€ clone ๋ฉ”์„œ๋“œ ๋ฐฉ์‹์ด ๊ฐ€์žฅ ๊น”๋”ํ•˜๋‹ค! 

 

 

 

 

14. Comparable ๊ตฌํ˜„ํ• ์ง€ ๊ณ ๋ ค 

  • Comparable ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฉ”์„œ๋“œ = compareTo
    • compareTo๋Š” ๋‹จ์ˆœ ๋™์น˜์„ฑ ๋น„๊ต, ์ˆœ์„œ ๋น„๊ต
    • ํ•„๋“œ์˜ ๊ฐ’ ๋น„๊ตํ•  ๋•Œ < ์™€ > ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ X 
    • ๋ฐ•์‹ฑ๋œ ๊ธฐ๋ณธ ํƒ€์ž… ํด๋ž˜์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ •์  compare ๋ฉ”์„œ๋“œ๋‚˜ Comparator ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
  • Comparable๋ฅผ ๊ตฌํ˜„ํ–ˆ์Œ = ๊ทธ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋“ค์—๋Š” ์ž์—ฐ์ ์ธ ์ˆœ์„œ ์žˆ์Œ
public interface Comparable<T> {
	int compareTo(T t);
}

 

 

โœณ๏ธ compareTo ๋ฉ”์„œ๋“œ์˜ ์ผ๋ฐ˜ ๊ทœ์•ฝ

  • ๊ฐ์ฒด๊ฐ€ ์ฃผ์–ด์ง„ ๊ฐ์ฒด๋ณด๋‹ค ์ž‘์œผ๋ฉด ์Œ์˜ ์ •์ˆ˜๋ฅผ, ๊ฐ™์œผ๋ฉด 0, ํฌ๋ฉด ์–‘์˜ ์ •์ˆ˜ ๋ฐ˜ํ™˜
  • ๋น„๊ตํ•  ์ˆ˜ ์—†๋Š” ํƒ€์ž…์˜ ๊ฐ์ฒด๊ฐ€ ์ฃผ์–ด์ง€๋ฉด ClassCastException ๋˜์ง

โœด๏ธ ๊ธฐ๋ณธ ํƒ€์ž… ํ•„๋“œ๊ฐ€ ์—ฌ๋Ÿฟ์ผ ๋•Œ์˜ ๋น„๊ต์ž

public int compareTo(PhoneNumber pn) {
	int result = Short.compare(areaCode, pn.areaCode); // ๊ฐ€์žฅ ์ค‘์š”ํ•œ ํ•„๋“œ
    if (result == 0) {
    	result = Short.compare(prefix, pn.prefix); // ๋‘ ๋ฒˆ์งธ๋กœ ์ค‘์š”ํ•œ ํ•„๋“œ
        if (result == 0) {
        	result = Short.compare(lineNum, pn.lineNum); // ์„ธ ๋ฒˆ์งธ๋กœ ์ค‘์š”ํ•œ ํ•„๋“œ
    }
    return result;
}

๐Ÿ‘‰ ๊ฐ€์žฅ ํ•ต์‹ฌ์ ์ธ ํ•„๋“œ๋ถ€ํ„ฐ ๋น„๊ตํ•ด๋‚˜๊ฐ€๊ธฐ (๋น„๊ต๊ฒฐ๊ณผ๊ฐ€ 0์ด ์•„๋‹ˆ๋ผ๋ฉด, ์ฆ‰ ์ˆœ์„œ๊ฐ€ ๊ฒฐ์ •๋˜๋ฉด ์—ฌ๊ธฐ์„œ ๋๋‚˜๋ฏ€๋กœ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ ๊ฐ€๋Šฅ)

 

 

โœด๏ธ ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•œ ๋น„๊ต์ž 

private static final Comparator<PhoneNumber> COMPARATOR = 
	comparingInt((PhoneNumber pn) -> pn.areaCode)
    	.thenComparingInt(pn -> pn.prefix)
        .thenComparingInt(pn -> pn.lineNum);

public int compareTo(PhoneNumber pn) {
	return COMPARATOR.compare(this, pn);
}

 

โœด๏ธ ์ •์  compare ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•œ ๋น„๊ต์ž

static Comparator<Object> hashCodeOrder = new Comparator<>() {
	public int compare(Object o1, Object o2) {
    	return Integer.compare(o1.hashCode(), o2.hashCode());
    }
}

 

โœด๏ธ ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•œ ๋น„๊ต์ž

static Comparator<Object> hasCodeOrder = 
	Comparator.comparingInt(o -> hashCode());