자바에서 Copy하는 것은 일반적으로 두 가지 시나리오에서 필요합니다.
1 . 첫 번째 시나리오는 기존 클래스의 객체의 인스턴스를 복사하여 동일한 데이터를 가진 새로운 인스턴스를 만드는 것.
2 . 두 번째 시나리오는 기존 클래스의 코드를 기반으로 새로운 클래스를 만드는 것.
자바에서 객체, 인스턴스를 복사하는 것은 방법은 몇 가지가 있지만,
가장 일반적인 방법은 "얕은 복사(Shallow Copy)"와 "깊은 복사(Deep Copy)"입니다.
각각의 방법은 객체 내부의 데이터를 어떻게 복사할지에 따라 다릅니다.
복사를 지원하는 클래스는 Cloneable 인터페이스[마커 인터페이스]를 클래스 선언부에 implements 해야 합니다.
Cloneable 인터페이스는 자바에서 복제 가능한 객체를 표시하기 위해 사용되는 마커 인터페이스입니다.
마커 인터페이스란, 메서드나 필드를 갖지 않고 단지 특정 기능 또는 속성을 가진다는 것을 나타내는 인터페이스입니다.
Cloneable 인터페이스는 복제가 지원되는 객체임을 나타내기 위해 클래스에서 구현됩니다.Cloneable 인터페이스를 구현하는 클래스는 복제 기능을 사용할 수 있습니다.
그러나 Cloneable 인터페이스는 메서드를 선언하지 않기 때문에 별도의 메서드를 구현해야 하는 것은 아닙니다.
대신, Cloneable 인터페이스를 구현한 클래스는 clone() 메서드를 호출할 수 있습니다.
clone() 메서드는 Cloneable 인터페이스를 구현한 클래스에서 상속받은 메서드로, 객체의 복제를 수행합니다.
clone() 메서드는 protected 접근 제어자를 가지고 있으므로, 해당 클래스에서 직접 호출할 수 있거나 하위 클래스에서 호출할 수 있습니다.
clone() 메서드를 호출하기 전에는 Cloneable 인터페이스를 구현한지 확인해야 합니다.
그렇지 않으면 CloneNotSupportedException이 발생할 수 있습니다.
Cloneable 인터페이스는 자바에서 복제 기능을 사용하기 위한 표시 역할을 하며, 이를 구현한 클래스는 객체의 복제를 지원합니다.
그러나 Deep Copy(깊은 복사)와 같은 세부 구현은 클래스에서 직접 처리해야 합니다.
- shallow copy은 MutableObject 복사 못함. 객체 복사는 못하고 자기자신만 복사 가능.
- deep copy는 필요한 경우 필드 내부 object 복사 가능.
다음은 얕은 복사와 깊은 복사를 수행하는 예제 코드입니다.
package com.kitec.copyobjects;
public class MutableObject implements Cloneable {
private int data;
public MutableObject(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package com.kitec.copyobjects;
public class ShallowCopyClass implements Cloneable {
private int value;
private MutableObject mutableObject;
public ShallowCopyClass(int value, MutableObject mutableObject) {
this.value = value;
this.mutableObject = mutableObject;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public MutableObject getMutableObject() {
return this.mutableObject;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package com.kitec.copyobjects;
public class DeepCopyClass implements Cloneable {
private int value;
private MutableObject mutableObject;
public DeepCopyClass(int value, MutableObject mutableObject) {
this.value = value;
this.mutableObject = mutableObject;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public MutableObject getMutableObject() {
return this.mutableObject;
}
@Override
protected Object clone() throws CloneNotSupportedException {
DeepCopyClass cloned = (DeepCopyClass) super.clone();
cloned.mutableObject = (MutableObject) mutableObject.clone(); // 필요한 경우 내부 객체도 복사
return cloned;
}
}
package com.kitec.copyobjects;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
MutableObject mutableObject = new MutableObject(10);
ShallowCopyClass shallowCopy = new ShallowCopyClass(5, mutableObject);
ShallowCopyClass shallowCopyClone = (ShallowCopyClass) shallowCopy.clone();
DeepCopyClass deepCopy = new DeepCopyClass(5, mutableObject);
DeepCopyClass deepCopyClone = (DeepCopyClass) deepCopy.clone();
// 얕은 복사의 경우, 필드 값은 그대로 공유됨
System.out.println(shallowCopy.getMutableObject().getData()); // 출력: 5
System.out.println(shallowCopyClone.getMutableObject().getData()); // 출력: 5
System.out.println(shallowCopy.getMutableObject() == shallowCopyClone.getMutableObject()); // 출력: true
shallowCopyClone.getMutableObject().setData(0);
// 깊은 복사의 경우, 필드 값들이 독립적으로 복사됨
System.out.println(deepCopy.getMutableObject().getData()); // 출력: 5
System.out.println(deepCopyClone.getMutableObject().getData()); // 출력: 5
System.out.println(deepCopy.getMutableObject() == deepCopyClone.getMutableObject()); // 출력: false
deepCopyClone.getMutableObject().setData(7);
}
}
이 시나리오에서는 기존 클래스의 코드를 기반으로 새로운 클래스를 만듭니다.
주로 클래스의 일부 수정이 필요하지만 기존 클래스의 구조를 재사용하고 싶을 때 사용됩니다.
자바에서는 리플렉션(Reflection)을 사용하여 클래스의 메타데이터를 분석하고 수정된 클래스를 생성할 수 있습니다.
아래는 클래스 복사를 수행하는 예제 코드
import java.lang.reflect.*;
class OriginalClass {
private int value;
public OriginalClass(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
// 사용 예시
public class Main {
public static void main(String[] args) throws Exception {
// 원본 클래스
OriginalClass original = new OriginalClass(5);
System.out.println(original.getValue()); // 출력: 5
// 클래스 복사
Class<?> originalClass = OriginalClass.class;
Class<?> copiedClass = Class.forName(originalClass.getName());
// 필드 수정
Field field = copiedClass.getDeclaredField("value");
field.setAccessible(true);
field.set(copiedClass.getDeclaredConstructor().newInstance(), 10);
// 수정된 클래스의 인스턴스 생성
OriginalClass copied = (OriginalClass) copiedClass.getDeclaredConstructor(int.class).newInstance(0);
System.out.println(copied.getValue()); // 출력: 10
}
}
이 예제에서는 원본 클래스 OriginalClass의 필드 값을 변경한 후, 수정된 클래스의 인스턴스를 생성하여 값을 확인합니다.
주의해야 할 점은 리플렉션을 사용할 때 예외 처리가 필요하며, 필드나 생성자의 접근성(accessibility)을 변경해야 할 수도 있습니다.
위의 예제 코드를 참고하여 클래스 복사를 원하는 시나리오에 맞게 적용하실 수 있습니다.