Java

[Java]Object 클래스의 clone 메서드

DevStory 2022. 9. 4.

Object 클래스의 clone 메서드

Java의 최상위 클래스인 Object에는 객체를 복제하는 clone() 메서드가 존재합니다. Java의 모든 클래스는 Object에서 파생되므로 모든 클래스의 인스턴스는 clone() 메서드를 사용할 수 있습니다.

 

clone() 메서드 구문은 다음과 같습니다.

protected native Object clone() throws CloneNotSupportedException;

clone() 메서드를 호출하려면 해당 객체의 클래스가 Cloneable 인터페이스를 구현해야 합니다. 만약, Cloneable 인터페이스를 구현하지 않으면 CloneNotSupportException이 발생합니다.

 

이번 포스팅은 Object 클래스의 clone 메서드를 사용하는 방법을 소개합니다.


clone 메서드의 필요성

clone() 메서드를 사용하여 객체를 복제하는 주요 목적은 기존 객체의 데이터를 보존하기 위함입니다. 기존 객체의 데이터가 변경되거나 손실되면 안 되므로 clone() 메서드로 객체를 복제 후 복제본의 데이터를 변경할 수 있습니다.

 

만약, clone() 메서드를 사용하지 않고 = 연산자를 사용하여 객체를 다른 객체에 할당 후 값을 변경하면, 기존 객체의 데이터도 변경되는 문제가 발생합니다.

 

다음 예제는 = 연산자를 사용하여 객체를 다른 객체에 할당 후 값을 변경합니다.

public static void main(String args[]) {
  Person person1 = new Person("둘리", 20);
  Person person2 = person1;

  System.out.println("[변경 전]");
  System.out.println(person1.toString());
  System.out.println(person2.toString());

  // person2의 Age 필드의 값 변경
  person2.setAge(100);

  System.out.println("\n[person2의 Age 필드의 값을 100으로 변경 후]");
  System.out.println(person1.toString());
  System.out.println(person2.toString());

  // person1의 Name 필드의 값 변경
  person1.setName("또치");

  System.out.println("\n[person1의 Name 필드의 값을 100으로 변경 후]");
  System.out.println(person1.toString());
  System.out.println(person2.toString());
}

[실행 결과]

[변경 전]
Person{Name='둘리', Age=20}
Person{Name='둘리', Age=20}

[person2의 Age 필드의 값을 100으로 변경 후]
Person{Name='둘리', Age=100}
Person{Name='둘리', Age=100}

[person1의 Name 필드의 값을 100으로 변경 후]
Person{Name='또치', Age=100}
Person{Name='또치', Age=100}

= 연산자를 사용하여 person1을 person2에 할당하면 동일한 참조 값을 가지므로 perons2의 Age 필드의 값을 수정하면, person1의 Age 필드의 값도 변경됩니다.

 

마찬가지로 person1의 Name 필드의 값을 수정하면, person2의 Name 필드의 값도 변경됩니다.


clone 메서드 사용 규칙

clone() 메서드를 호출하기 위해서는 다음 규칙을 따라야 합니다.

protected native Object clone() throws CloneNotSupportedException;

- 객체의 클래스가 Cloneable 인터페이스를 구현해야 합니다. 구현하지 않으면, CloneNotSupportedException이 발생합니다.

- clone() 메서드의 반환 타입은 Object입니다. 따라서, 타입 캐스팅이 필요합니다.

- clone() 메서드는 CloneNotSupportedException을 throw 합니다. 따라서, clone() 메서드를 호출하는 코드에서 try~catch문을 사용하여 예외를 처리하거나 throw 키워드를 사용합니다.

- 접근 제어자가 protected이므로 동일한 패키지 내부가 아니거나 파생 클래스가 아닌 경우 호출할 수 없습니다. 동일한 패키지가 아닌 경우 clone() 메서드를 재정의해야 합니다.

 

즉, 사진처럼 MainForm 클래스와 Person 클래스는 다른 패키지에 존재하므로 MainForm 클래스에서 Person 타입의 객체에서 clone() 메서드를 호출할 수 없습니다.

 

MainFrom 클래스에서 Person 클래스의 clone() 메서드를 호출하려면, Person 클래스에서 clone() 메서드를 재정의해야 합니다.


clone 메서드 재정의

clone() 메서드를 재정의하는 절차는 다음과 같습니다.

1. clone() 메서드의 로직은 변경되면 안 됩니다. 따라서, super 키워드를 사용하여 clone() 메서드를 호출합니다.

2. super.clone() 메서드는 Object타입을 반환합니다. clone() 메서드를 호출할 때마다 타입 캐스팅하는 코드를 작성하지 않도록 재정의된 clone() 메서드에서 타입 캐스팅하는 코드를 추가합니다.

 

다음 소스 코드는 clone() 메서드가 재정의된 Person 클래스입니다.

public class Person implements Cloneable {
  private String Name;
  private Integer Age;

  public Person(String name, Integer age) {
    Name = name;
    Age = age;
  }

  public String getName() {
    return Name;
  }

  public void setName(String name) {
    Name = name;
  }

  public Integer getAge() {
    return Age;
  }

  public void setAge(Integer age) {
    Age = age;
  }

  @Override
  public String toString() {
    return "Person{" +
            "Name='" + Name + '\'' +
            ", Age=" + Age +
            '}';
  }

  @Override
  public Person clone() throws CloneNotSupportedException {
    return (Person) super.clone();
  }
}

 

다음 예제는 Person 클래스의 clone() 메서드를 사용하여 객체를 복제하고 필드의 값을 변경합니다.

public static void main(String args[]) {
  try {
    Person person1 = new Person("둘리", 20);
    Person person2 = person1.clone();

    System.out.println("[변경 전]");
    System.out.println(person1.toString());
    System.out.println(person2.toString());

    // person2의 Age 필드의 값 변경
    person2.setAge(100);

    System.out.println("\n[person2의 Age 필드의 값을 100으로 변경 후]");
    System.out.println(person1.toString());
    System.out.println(person2.toString());

    // person1의 Name 필드의 값 변경
    person1.setName("또치");

    System.out.println("\n[person1의 Name 필드의 값을 100으로 변경 후]");
    System.out.println(person1.toString());
    System.out.println(person2.toString());
  } catch (Exception ex) {
    System.out.println("예외 발생");
  }
}

[실행 결과]

[변경 전]
Person{Name='둘리', Age=20}
Person{Name='둘리', Age=20}

[person2의 Age 필드의 값을 100으로 변경 후]
Person{Name='둘리', Age=20}
Person{Name='둘리', Age=100}

[person1의 Name 필드의 값을 100으로 변경 후]
Person{Name='또치', Age=20}
Person{Name='둘리', Age=100}

복제된 객체의 값을 변경해도 기존 객체에 영향이 없는 것을 확인할 수 있으며, 반대로 기존 객체의 값을 변경해도 복제된 객체에 영향이 없습니다.

반응형

댓글