결론부터 말하자면  clone 쓰지맙시다!
물론 꼭 필요하면 쓰긴 해야겠지만..

clone 하면 뭐가 가장 먼저 떠오르시나요?
빡빡이 아저씨가 생각났다면 다시한번 다른걸 생각해봅시다. 철자도 달라요

복제양 둘리, 복제인간 이런게 생각났다면 잘 찾아 오신겁니다.
간단히 말해서 copy. 복사 하는겁니다.

프로그래밍을 기초보다 조금더 공부하셨다면 깊은복사얕은복사 라는 이야기를 들어봤을겁니다. 못들어봤다면 조금더 공부하면 나올겁니다.

일반적으로 복사한다 라고하면
int i=10;
int b;
b=i;
이런식으로  b에 i가 가지고 있는 값 10을 복사 하는겁니다.

그런데 A라는 클레스가 있다면
A a1=new A();
A a2;
a2=a1;

이라고 했을때 여기서 a2는 a1과 무슨 관계일까요?
a1과 a2는 그저 메모리상에 생성된 클레스A의 동일한 객체를 가르키는 변수일 뿐입니다.
이걸 복사라고 말하고 싶은건 아니겠죠? - 물론 이게 목적인경우도 있습니다.

제가 종종 프로그래밍을 처음하는 친구들에게 변수는 메모지와도 같다. 라는 이야기를 하는데
메모지에다가 집주소를 적었습니다. 그게 a1이라는 메모지입니다.
a2 라는 종이 쪼가리에다가 a1에 적혀있는 집주소를 똑같이 적었습니다.

집이 두체가 되었어요!!

라고 놀라시면 안됩니다.

이것은 그저 그 집의 주소를 두군데다가 적었을 뿐입니다. 정말 얕디 얕은 복사죠

그렇다면 우리집을 복사하기 위해서는 어떻게 해야할까요?
우선 집터를 구합니다. 그리고 우리집의 설계도면도 필요할것이고 어떤 재료로 지어졌는지도 알아야합니다. 그리고 집지을 인부도 필요하겠죠. 그렇게 집을 다 지은 다음에 다른 종이에다가 새집의 주소를 적으면 정말 집이 복사된겁니다!
-물론 집이라는 껍데기만 같을뿐 내용물은... 이런 비슷한(정말 비슷하기만한) 이유로 깊은복사와 얕은 복사가 발생합니다.

컴퓨터에서도 마찬가지입니다. 어떤 객체를 복사! 하기 위해서는 참조변수만 하나 더 만드는것이 아니라 아예 메모리상에다가 똑같은놈을 하나 더 만드는겁니다.

그렇다면 똑같이 생긴놈을 만드는 어떤 방법이 있을까요?

java에는 Object 클레스에서 내려오는 clone 라는 메소드가 있습니다.
이걸 오버라이드 해서 clone를 구현하라고 나옵니다. 근데 그냥 오버라이드 해서 호출하면 에러납니다.
Cloneable 라는 인터페이스를 구현해줘야합니다. 근데 implements 했는데 구현할게 업서영
Cloneable 은 Serializable 같은 인터페이스로 그냥 이런거 됩니다. 라고 표시해주는 역활을 합니다. 하라니까 하는겁니다!
그리고는

public class Test implements Cloneable {
	int i;
	public Test(Test t){
		i=t.i;
	}
	@Override
	protected Test clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		Test t=(Test)super.clone();
		t.i=i;
		return t;
	}
}

이런식으로 clone 메소드를 만들어 줄수 있습니다.
클론 메소드 작성에는 몇가지 규칙이 있는데
x.clone()!=x 이어야하고
x.getClass()==x.clone().getClass() 이어야합니다.
그리고 경우에 따라서 x.equals(x.clone()) 도 true 일수 있습니다.
또한가지는 절대로 생성자를 쓰면 안된다는겁니다.
객체 생성을 new 를 쓰지말고 super.clone()을 호출해서 만들라는 겁니다.
참조 : http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#clone()
그리고 super.clone()로 만든 객체를 현재 객체로 캐스팅한뒤 값을 조작한다?

하여튼 이래저래 마음에 안듭니다. 어쨋든 일단 복사는 되었으니 재껴둡시다.

위에서 깊은복사 얕은 복사 이야기 했죠? 지금 위에 있는 저것도 아직 얕은 복사입니다.
만약 책에서 예를 든것처럼 스택이라는 클레스를 만들었다고 가정해봅시다.
Object 배열을 만든다음에 거기에 인덱스를 이용해서 스택을 구현했습니다.

public class Stack implements Cloneable {
	Object [] elements=new Object[100];
	int index=0;
	
	public void push(Object e){}
	public Object pop(){}
	@Override
	protected Stack clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		Stack s=(Stack)super.clone();
		s.index=index;
		s.elements=elements;
		return s;
	}
}
이런식으로 스택을 만들고 clone를 구현했다고 해봅시다.
여기서 어떤 문제가 발생할까요? 만약 원본, 혹은 복사된 객체에서 push나 pop을 했다면 elements 배열에 접근해서 조작을 하게 되는데 그것이 원본/복사본 에 영향을 바로 미친다는겁니다.
따라서 
s.elements=elements;
부분을
s.elements=Arrays.copyOf(elements, elements.length); 혹은
s.elements=elements.clone();
이런 식으로 바꾸어줄 필요가 있습니다.

이걸로 깊은복사가 모두 되었을까요?
또한가지 문제점이 있습니다. 만약에 링크드 리스트를 구현했다고 해봅시다.
A1->A2->A3->A4 이런식으로 물려있다고 합시다.
복사를 할때 A1만을 clone를 통해서 만들었다면 나머지 A2,A3,A4등은 원본과 복사본 모두에서 동시에 참조하고 있기 때문에 잘못 건드렸다간 안드로메다로 날아가버릴 공산이 큽니다.
이것들을 모두 복사해서 만들어줄 필요성이 생깁니다. 가장 먼저 생각할수 있는 방법은 모든 리스트를 순회하면서 새로운 리스트에 복사해서 집어넣는겁니다.

게다가 혹시나 final 맴버변수가 있다면 이것참 골때립니다.super.clone()로 객체를 만든다음 현재 클레스로 변환하고 그 객체의 final 맴버변수를 현재 객체의 값과 같은걸로 바꿔야 하는데 final 이잔아? 안될거야.. 결국 final을 때야됩니다.

하여튼 프로그래밍에서 복사라는 동작은 이처럼 지랄귀찬습니다.

하지만 clone를 사용하지않고 복사본을 만드는 다른 방법이 있습니다.

복제 생성자를 이용하는 방법입니다. 생성자에 파라미터를 자신과 같은 클레스로 한다음에 생성할때 각각 값을 복사하는 겁니다.

public class Test {
	int i;
	public Test(Test t){
		i=t.i;
	}
	public Test copy(){
		return new Test(this);
	}
}

이렇게 copy 라는 메소드를 만들수도 있습니다.
사실을 copy 같은거 만들 필요도 없습니다. 복사가 필요한 위치에서 new Test(t); 같은 형식으로 복사 생성해버리면 되거든요.
여태까지 clone를 장황하게 설명해놓고 왜 이게 튀어나오냐고요?
서두에 말했잔아요 clone 같은거 쓰지말자고..

하여튼 복사 생성자는 clone에서 발생하는 문제점(케스팅을 해야된다고! 와 final 맴버변수, 예외처리등)을 모두 해결하면서도 쉽게 이해가 가능합니다.
게다가 clone 라는걸 만들어 놓았으면서도 자바에서는 복사 생성자를 사용하는경우가 상당합니다.
예를들어 Collection 클레스들이 대표적입니다.
HashMap 라는 Map 클레스는 생성자에서 Map 를 전달 받을수 있습니다. 즉 Map 객체를 받아서 그걸 복사한 객체를 새로 생성하겠다는거죠. List나 Set도 마찬가지입니다.
따라서 HashSet으로 만든 객체를 TreeSet로 복사하고 싶다. 라면 clone에서는 죽었다 깨어나도 못할짓이겠지만 복사생성자에서는 생성자의 인자로 Set 을 받아버리면 만사 OK라는겁니다. 실제로도 그렇게 구현되어있구요

결국 clone 메소드를 사용하는건 배열의 복사 정도 밖에 없습니다. 그조차도 Arrays.copyOf 를 쓰면되지만 이거보다는 clone가 짧으니까..

물론 clone에서 깊은복사 문제는 복사 생성자에서도 동일하게 적용됩니다. 할건 해야된다는거죠.

하여튼 만일 clone 메소드를 오버라이드 해야할경우에는 정말 이게 clone만으로 해야하는가? 복사 생성자는 못쓰는건가? 라는 심도 있는 고민을 해봐야 할것입니다.

결국 clone 메소드는 오버라이드 하지 말자! 가 결론입니다. 왜 만든거야 ㅅㅂ!

여기서 나오는 또한가지 의문. 그러면 clone 메소드에다가 복사생성자로 리턴하면 안되나?
뭐.. 안될건 없습니다. 없습니다만은..
만약 java에서 명시한 규칙을 지키지 않고 생성자를 통해서 clone 를 구현했다면 차후에 자바의 버전이 올라가면서 Object 의 clone 구현 방식이 달라지고 새로 생긴 API 에서 그것에 맞추어서 사용한다. 가 되어버리면 지금 만들어 놨던거 싹 갈아 엎어야 할지도 모릅니다.
뭐 간단 간단하게 쓰는데야 이러나 저러나 뭔상관 이겠습니까만.. 미래를 위해서 말이죠 
Posted by 동적할당
: