본문 바로가기

Java

Java clone()에 대한 예제 하나만 더


http://www.codeguru.com/java/tij/tij0127.shtml 여기에 있는 예제입니다.

포스트를 작성하고나서 풍대리님이 url을 보내주셔서 한번 보았는데

예제가 참 좋습니다.

그중 하나면 여기다 올려보겠습니다.


//: Snake.java
// Tests cloning to see if destination of
// handles are also cloned.

public class Snake implements Cloneable {
  private Snake next;
  private char c;
  // Value of i == number of segments
  Snake(int i, char x) {
    c = x;
    if(--i > 0)
      next = new Snake(i, (char)(x + 1));
  }
  void increment() {
    c++;
    if(next != null)
      next.increment();
  }
  public String toString() {
    String s = ":" + c;
    if(next != null)
      s += next.toString();
    return s;
  }
  public Object clone() {
    Object o = null;
    try {
      o = super.clone();
    } catch (CloneNotSupportedException e) {}
    return o;
  }
  public static void main(String[] args) {
    Snake s = new Snake(5, 'a');
    System.out.println("s = " + s);
    Snake s2 = (Snake)s.clone();
    System.out.println("s2 = " + s2);
    s.increment();
    System.out.println(
      "after s.increment, s2 = " + s2);
  }
} ///:~
이 예제입니다.

한번 돌려보세요~ 결과가 아래와 같이 나옵니다.

s = :a:b:c:d:e
s2 = :a:b:c:d:e
after s.increment, s2 = :a:c:d:e:f

재귀호출을 이용해서 snake 클래스를 마치 linked list 처럼 만들어내고 있습니다. 5개의 연결 객체를 만들어냅니다.

Snake s = new Snake(5, 'a');


에 의해서 객체 a가 생성됩니다. 이녀석은 마치

s: c = a
   - s: c = b
       - s: c =  c
             - s: c = d 
                   - s: c = e


이런 구조로 되어있을 것입니다. Snake를 멤버로 가지고 있으니까요.

그 다음 s2에 s를 clone합니다. 이 시점에서 s2는 위 s와 동일한 구조를 가지게 됩니다.


s2: c = a
   - s: c = b
       - s: c =  c
             - s: c = d 
                   - s: c = e

여기까지는 큰 어려움은 없구요...
잘 보시면 첫번째 객체는 s2인데 이후에 달린 next들은 전부 s입니다.

일단 이상태에서 이제 s.increment()를 실행합니다.

대충 짐작은 갑니다. clone()을 한 객체가 서로 영향을 받느냐 안 받느냐..

처음에 저는 영향을 받는다면 b,c,d,e,f가 나올 것이고 영향을 안 받는다면 a,b,c,d,e가 나올 것이다..
그냥 통밥으로 그렇게 때려맞췄었던거죠..

그런데 결과는

s2 = :a:c:d:e:f 입니다. 물론 s는 s = :b:c:d:e:f 입니다.

왜 이렇게 결과가 나올까요..

일단 c가 char 즉, immutable이라는 것입니다.

Snake s2 = s.clone();

을 하게 되면 s의 복사를 하기 위해 Snake type의 껍데기를 만들고
s( c가 a를 가지고 있고 Snake:c=b인 녀석을 next로 가지고 있는)를 복사하게 되는데 c는 immutable이므로 별도의 메모리에 값이 할당됩니다.

즉, s2.c와 s.c는 전혀 별개의 값을 갖게 되는 것입니다.

하지만, next는 좀 상황이 다릅니다.

s를 s2에 복사를 하게 되면서 c는 위의 말대로 복사가 되었지만 next는 mutable 객체이기 때문에
참조값을 그대로 복사하게 됩니다. 즉 s2.next와 s.next는 같은 참조값을 가지게 되고 결국 s2.next(그리고 그 이후에 줄줄이 달린 next들)는 s.next와 동일한녀석이 되는 것입니다.

결국 clone()에 의한 복사는 첫번째 segment에서만 진행이 된 것이고 나머지는 복사라는 과정 자체가 진행되지 않았지만, 첫번째 복사가 진행되면 s2.next와 s.next가 같은 참조값을 가지게 되었기 때문에 위와 같은 결과가 나오게 되는 것입니다.


PS. 첫번째 포스팅에 오류가 있어고 풍대리님이 바로 잡아주셨습니다.