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

Clone ๋ฉ”์„œ๋“œ

์›๋ณธ ๊ฐ์ฒด์˜ ํ•„๋“œ๊ฐ’๊ณผ ๋™์ผํ•œ ๊ฐ’์„ ๊ฐ€์ง€๋Š” ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ๋Š” ๋ฉ”์„œ๋“œ

Shallow ๋ณต์‚ฌ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋งŒ์•ฝ ๊ฐ์ฒด๊ฐ€ ์ฐธ์กฐํƒ€์ž…์˜ ๋ฉค๋ฒ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด ์ฐธ์กฐ๊ฐ’๋งŒ ๋ณต์‚ฌํ•˜๋Š” ํŠน์ง•์ด ์žˆ๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ์—์„œ s1๊ณผ s2๋Š” ๊ฐ™์€ name ํ•„๋“œ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋˜์ง€๋งŒ, s1 == s2๋Š” false ์ด๋‹ค.

class Student implements Cloneable{
    String name;
    Student(String name){
        this.name = name;
    }
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

class Main {
    public static void main(String[] args) {
        Student s1 = new Student("ํ™๊ธธ๋™");
        try {
            Student s2 = (Student)s1.clone();
            System.out.println(s1.name);
            System.out.println(s2.name);
						System.out.println(s1 == s2);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

Cloneable์˜ ์—ญํ• 

Cloneable์€ ๋ณต์ œํ•ด๋„ ๋˜๋Š” ํด๋ž˜์Šค์ž„์„ ๋ช…์‹œํ•˜๋Š” ์šฉ๋„๋กœ ์“ฐ์ธ๋‹ค. Cloneable ์ธํ„ฐํŽ˜์ด์Šค ๋‚ด์—๋Š” clone ๋ฉ”์„œ๋“œ๊ฐ€ ์—†๋‹ค. clone ๋ฉ”์„œ๋“œ๊ฐ€ ์„ ์–ธ๋œ ๊ณณ์€ Object์ด๋ฉฐ protected์ด๊ธฐ์—, Coneable์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ์™ธ๋ถ€ ๊ฐ์ฒด์—์„œ clone ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค.

๋ฉ”์„œ๋“œ ํ•˜๋‚˜ ์—†๋Š” Cloneable ์ธํ„ฐํŽ˜์ด์Šค๋Š” Object์˜ protected ๋ฉ”์„œ๋“œ์ธ clone์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ๊ฒฐ์ •ํ•œ๋‹ค.

  • Cloneable์„ ๊ตฌํ˜„ โ†’ ๊ฐ์ฒด์˜ ํ•„๋“œ๋“ค์„ ํ•˜๋‚˜ํ•˜๋‚˜ ๋ณต์‚ฌํ•œ ๊ฐ์ฒด ๋ฐ˜ํ™˜
  • Cloneable์„ ๋ฏธ๊ตฌํ˜„ โ†’ CloneNotSupportedException์„ ๋˜์ง

Cloneable ์‚ฌ์šฉ ์‹œ ์ฃผ์˜ํ•  ์ 

1. ๊ฐ€๋ณ€ ๊ฐ์ฒด ์ฐธ์กฐ CASE

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

	public Stack() {
		this.elements = new Object[DEFAULT_INITIAL_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;
	}

	// ์›์†Œ๋ฅผ ์œ„ํ•œ ๊ณต๊ฐ„์„ ์ ์–ด๋„ ํ•˜๋‚˜ ์ด์ƒ ํ™•๋ณดํ•œ๋‹ค.
	private void ensureCapacity() {
		if (elements.length == size)
		elements = Arrays.copyOf(elements, 2 * size + 1);
	}
}

์œ„ ํด๋ž˜์Šค๋ฅผ ๋ณต์ œ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค๋ฉด, array ํƒ€์ž…์˜ elements ํ•„๋“œ๋Š” ์›๋ณธ๊ณผ ๊ฐ™์€ ๋ฐฐ์—ด์„ ์ฐธ์กฐํ•˜๊ฒŒ ๋œ๋‹ค. ์ฆ‰, ๋ฐฐ์—ด์„ ๋ณต์‚ฌํ•  ๋•Œ ๋‹จ์ˆœํžˆ clone()๋งŒ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด ์ž˜๋ชป๋œ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ ๋‹ค.

์ด๋Ÿฌ๋ฉด ๋ถˆ๋ณ€์‹์„ ํ•ด์น˜๊ฒŒ ๋˜๊ณ  ํ”„๋กœ๊ทธ๋žจ์ด ์ด์ƒํ•˜๊ฒŒ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋”ฐ๋ผ์„œ clone ๋ฉ”์„œ๋“œ๋Š” ๋ณธ ๊ฐ์ฒด์— ์•„๋ฌด๋Ÿฐ ํ•ด๋ฅผ ๋ผ์น˜์ง€ ์•Š๋Š” ๋™์‹œ์— ๋ณต์ œ๋œ ๊ฐ์ฒด์˜ ๋ถˆ๋ณ€์‹์„ ๋ณด์žฅํ•ด์•ผ ํ•œ๋‹ค.

์˜ฌ๋ฐ”๋ฅธ ๋ฐฐ์—ด ๋ณต์‚ฌ

๋”ฐ๋ผ์„œ array type์„ ํ•„๋“œ๋กœ ๊ฐ–๋Š” class๋Š” ๋ฐฐ์—ด์„ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฐ๊ฐ cloneํ•ด์•ผ ํ•œ๋‹ค.

@Override public Stack clone() {
	try {
		Stack result = (Stack) super.clone();
		result.elements = elements.clone();
		return result;
	} catch (CloneNotSupportedException e) {
		throw new AssertionError();
	}
}

2. Stack Overflow CASE

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

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

		Entry(Object 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];
			for (int i = 0; i < buckets.length; i++) {
				if (buckets[i] != null)
					result.buckets[i] = buckets[i].deepCopy();
			return result;
		} catch (CloneNotSupportedException e) {
			throw new AssertionError();
		}
	}
	... // ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋Š” ์ƒ๋žต
}

์œ„ ์˜ˆ์ œ์—์„œ HashTable ํด๋ž˜์Šค๋Š” buckets์ด๋ผ๋Š” Entry ํƒ€์ž…์˜ array๋ฅผ ํ•„๋“œ๋กœ ๊ฐ€์ง„๋‹ค. Entry ํƒ€์ž…์€ next ํ•„๋“œ๋ฅผ ํ†ตํ•ด ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋‹ค.

์œ„ ์ฝ”๋“œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“œ์‹œ, HashTable clone ๋ฉ”์„œ๋“œ๋Š” deep copy๋ฅผ ์ง€์›ํ•œ๋‹ค. clone ๋ฉ”์„œ๋“œ๋Š” ๋จผ์ € ์ ์ ˆํ•œ ํฌ๊ธฐ์˜ ์ƒˆ๋กœ์šด ๋ฒ„ํ‚ท ๋ฐฐ์—ด์„ ํ• ๋‹นํ•˜๊ณ , ๊นŠ์€ ๋ณต์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ด ๋•Œ Entry์˜ deepCopy ๋ฉ”์„œ๋“œ๋Š” ์ž์‹ ์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ ์ „์ฒด๋ฅผ ๋ณต์‚ฌํ•˜๊ธฐ ์œ„ํ•ด ์ž์‹ ์„ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณต์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ์ ์ ˆํ•˜์ง€ ์•Š๋‹ค. ์žฌ๊ท€ ํ˜ธ์ถœ ๋•Œ๋ฌธ์— ๋ฆฌ์ŠคํŠธ์˜ ์›์†Œ ์ˆ˜๋งŒํผ ์Šคํƒ ํ”„๋ ˆ์ž„์„ ์†Œ๋น„ํ•˜์—ฌ, ๋ฆฌ์ŠคํŠธ๊ฐ€ ๊ธธ๋ฉด ์Šคํƒ ์˜ค๋ฒ„ํ”Œ๋กœ๋ฅผ ์ผ์œผํ‚ฌ ์œ„ํ—˜์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์€ deepCopy๋ฅผ ์žฌ๊ท€ ํ˜ธ์ถœ ๋Œ€์‹  ๋ฐ˜๋ณต์ž๋ฅผ ์“ฐ๋Š” ๊ฒƒ์ด๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ ์ฐธ์กฐ.

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 ์‚ฌ์šฉ๋ฒ• ์ •๋ฆฌ

Cloneable์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ชจ๋“  ํด๋ž˜์Šค๋Š” clone์„ ์žฌ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค. ์ด๋•Œ ์ ‘๊ทผ ์ œํ•œ์ž๋Š” public์œผ๋กœ, ๋ฐ˜ํ™˜ ํƒ€์ž…์€ ํด๋ž˜์Šค ์ž์‹ ์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ๊ฐ€์žฅ ๋จผ์ € super.clone์„ ํ˜ธ์ถœํ•œ ํ›„ ํ•„์š”ํ•œ ํ•„๋“œ๋ฅผ ์ „๋ถ€ ์ ์ ˆํžˆ ์ˆ˜์ •ํ•œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์ด ๋ง์€ ๊ทธ ๊ฐ์ฒด์˜ ๋‚ด๋ถ€ '๊นŠ์€ ๊ตฌ์กฐ'์— ์ˆจ์–ด ์žˆ๋Š” ๋ชจ๋“  ๊ฐ€๋ณ€ ๊ฐ์ฒด๋ฅผ ๋ณต์‚ฌํ•˜๊ณ , ๋ณต์ œ๋ณธ์ด ๊ฐ€์ง„ ๊ฐ์ฒด ์ฐธ์กฐ ๋ชจ๋‘๊ฐ€ ๋ณต์‚ฌ๋œ ๊ฐ์ฒด๋“ค์„ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ํ•จ์„ ๋œปํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ๋‚ด๋ถ€ ๋ณต์‚ฌ๋Š” ์ฃผ๋กœ clone์„ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•ด ๊ตฌํ˜„ํ•˜์ง€๋งŒ, ์ด ๋ฐฉ์‹์ด ํ•ญ์ƒ ์ตœ์„ ์ธ ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ํƒ€์ž… ํ•„๋“œ์™€ ๋ถˆ๋ณ€ ๊ฐ์ฒด ์ฐธ์กฐ๋งŒ ๊ฐ–๋Š” ํด๋ž˜์Šค๋ผ๋ฉด ์•„๋ฌด ํ•„๋“œ๋„ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋‹จ, ์ผ๋ จ๋ฒˆํ˜ธ๋‚˜ ๊ณ ์œ  IDsms ๋น„๋ก ๊ธฐ๋ณธ ํƒ€์ž…์ด๋‚˜ ๋ถˆ๋ณ€์ผ์ง€๋ผ๋„ ์ˆ˜์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

Cloneable ๋Œ€์•ˆ

๋ณต์‚ฌ ์ƒ์„ฑ์ž์™€ ๋ณต์‚ฌ ํŒฉํ„ฐ๋ฆฌ๋ผ๋Š” ๋” ๋‚˜์€ ๊ฐ์ฒด ๋ณต์‚ฌ ๋ฐฉ์‹์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ณต์‚ฌ ์ƒ์„ฑ์ž๋ž€ ๋‹จ์ˆœํžˆ ์ž์‹ ๊ณผ ๊ฐ™์€ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ์ƒ์„ฑ์ž๋ฅผ ๋งํ•œ๋‹ค.

// ๋ณต์‚ฌ ์ƒ์„ฑ์ž
public Yum(Yum yum) {...};
// ๋ณต์‚ฌ ํŒฉํ„ฐ๋ฆฌ
public static Yum newInstance(Yum yum) {...};

๋ณต์‚ฌ ์ƒ์„ฑ์ž์™€ ๊ทธ ๋ณ€ํ˜•์ธ ๋ณต์‚ฌ ํŒฉํ„ฐ๋ฆฌ๋Š” Cloneable๊ณผ Clone ๋ฐฉ์‹๋ณด๋‹ค ๋‚˜์€ ์ ์ด ๋งŽ๋‹ค.

์–ธ์–ด ๋ชจ์ˆœ์ ์ด๊ณ  ์œ„ํ—˜์ฒœ๋งŒํ•œ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฉ”์ปค๋‹ˆ์ฆ˜(์ƒ์„ฑ์ž๋ฅผ ์“ฐ์ง€ ์•Š๋Š” ๋ฐฉ์‹)์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ, ์—‰์„ฑํ•˜๊ฒŒ ๋ฌธ์„œํ™”๋œ ๊ทœ์•ฝ์— ๊ธฐ๋Œ€์ง€ ์•Š๊ณ , ์ •์ƒ์ ์ธ final ํ•„๋“œ ์šฉ๋ฒ•๊ณผ๋„ ์ถฉ๋Œํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋ถˆํ•„์š”ํ•œ ๊ฒ€์‚ฌ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€์ง€ ์•Š๊ณ , ํ˜•๋ณ€ํ™˜๋„ ํ•„์š”์น˜ ์•Š๋‹ค.

๋˜ํ•œ, ๋ณต์‚ฌ ์ƒ์„ฑ์ž์™€ ๋ณต์‚ฌ ํŽ™ํ„ฐ๋ฆฌ๋Š” ํ•ด๋‹น ํด๋ž˜์Šค๊ฐ€ ๊ตฌํ˜„ํ•œ '์ธํ„ฐํŽ˜์ด์Šค' ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋“ค์„ ์ด์šฉํ•˜์—ฌ HashSet ๊ฐ์ฒด s๋ฅผ TreeSet ํƒ€์ž…์œผ๋กœ ๋ณต์ œํ•  ์ˆ˜ ์žˆ๋‹ค.