Obejct 클래스에는 clone() 메소드가 정의되어 있습니다. clone 메소드는 해당하는 객체의 모든 필드를 복사하여 새로운 객체에 넣어 반환하는 동작을 수행합니다.
즉, 대상을 똑같이 만들어주는 기능입니다.
여기에는 의무사항이 있는데, clone 메소드의 호출을 허용하려면 Cloneable 인터페이스를 구현해야 합니다.
class Point implements Cloneable { // 복사할 수 있는 Point 클래스 생성
private int xPos; // 변수 xpos; 선언
private int yPos; // 변수 ypos; 선언
이렇게 클론 메소드의 호출을 허용하려는 클래스에는 Clonable 인터페이스를 구현해야 합니다.
protected native Object clone() throws CloneNotSupportedException;
상기 코든느 Object 내의 clone() 메소드 코드이고, CloneNotSupportedException를 예외 처리 합니다.
Cloneable 인터페이스는 마커 인터페이스 입니다.
마커 인터페이스 -> 이름을 붙임으로써 의미를 부여해야 하는 것. 부모를 추가해야하는 것.
즉, 추상 메소드와 같이 동작이 하나도 존재하지 않는 인터페이스입니다.
Cloneable 인터페이스를 상속implements 함으로써 Object 의 clone 메소드를 따호 호출하지 않고, clone 메소드를 재정의 합니다.
여기에서 확인할 것! 자바의 복사 작업은 두 가지로 구분합니다.
- 얕은 복사 : 값은 같다. 변수만 추가 Shallow copy
하나의 대상을 같이 보는 것이므로 모든 참조변수 대상에게 변경이 적용 되는 위험이 있습니다.
같은 대상을 보고 있으므로 오리지널을 변경할 경우 카피본도 변경이 됩니다.
- 깊은 복사 : 메모리 추가 할당. 변수도 추가 Deep copy
동일한 값을 가진 인스턴스를 새롭게 만들어서 참조를 시켜 새로운 메모리를 할당하는 것입니다.
얕은 복사의 대상까지도 모두 새롭게 생성을 해야 합니다.
참조 자료형에 대해 클론을 할 때에는 기본적으로 얕은 복사가 되므로 깊은 복사를 원할 경우 로직을 추가해서 구현해야 합니다.
@Override
public Object clone() throws CloneNotSupportedException {
Person cpy = (Person)super.clone(); // clone 메소드 호출을 통한 복사본 생성
cpy.name = new String(name); // 깊은 복사의 형태로 복사본을 완성
return cpy; // 완성된 복사본의 참조 값 반환
}
(스트링 인스턴스를 대상으로 한 깊은 복사가 가능하지만 스트링의 경우 결국 원본에 대한 수정이 불가능하기 때문에
얕은 복사만 하여도 무방합니다.)
클론을 해서 반환을 받으면 명시적 형 변환을 해주어야 합니다.
클론에 한해서만 오버라이딩할 때 반환형을 바꿔서 할 수 있습니다.
하단의 예제를 보면서 확인해보겠습니다. 설명은 각 각주를 확인 부탁드립니다.
class Point implements Cloneable { // 복사할 수 있는 Point 클래스 생성
private int xPos; // 변수 xpos; 선언
private int yPos; // 변수 ypos; 선언
public Point(int x, int y) { // 변수 초기화
xPos = x;
yPos = y;
}
public void showPosition() { //Point 클래스 안의 showpositon 메소드 추가
System.out.printf("[%d, %d]", xPos, yPos); // 각각의 x와 y값 출력하는 좌표
System.out.println();
}
public void changePos(int x, int y) { //Point 클래스 안에 changepos 메소드 추가
xPos = x; // x와 y값의 값을 변경하여 비교하고자 하는 클래스입니다.
yPos = y; //
}
@Override
public Object clone() throws CloneNotSupportedException { // clone()메소드를 Object 로 형 변환을 시켜줍니다.
return super.clone();
}
}
class Rectangle implements Cloneable { // Ractangle 클래스 생성 복사할 수 있는 인스턴스
private Point upperLeft; // 좌측 상단 좌표로 Point 의 변수 선언
private Point lowerRight; // 우측 하단 좌표 Point 의 변수 선언
public Rectangle(int x1, int y1, int x2, int y2) { // 클래스 안에서 좌표에 값을 넣는 메소드 생성
upperLeft = new Point(x1, y1);
lowerRight = new Point(x2, y2);
}
// 좌표 정보를 수정함
public void changePos(int x1, int y1, int x2, int y2) { //좌표의 값을 수정 변경
upperLeft.changePos(x1, y1);
lowerRight.changePos(x2, y2);
}
@Override
public Object clone() throws CloneNotSupportedException { // Object 내의 clone() 메서드 코드에 CloneNotSupportedException를 예외 처리 합니다.
return super.clone(); // super.clone() 체인으로 클론 메소드를 호출
// Object 의 clone 메소드는 protected 단계로 접근을 제한하기 때문에 재정의가 필요합니다.
// clone()을 재정의 하지 않고 사용하면 얕은 복사만 가능합니다.
}
// 직사각형 좌표 정보 출력
public void showPosition() { // Rectangle 클래스 안에 showPosition 메소드를 추가합니다.
System.out.print("좌측 상단: ");
upperLeft.showPosition();
System.out.print("우측 하단: ");
lowerRight.showPosition();
System.out.println();
}
}
class ShallowCopy { // ShallowCopy 클래스를 생성합니다.
public static void main(String[] args) {
Rectangle org = new Rectangle(1, 1, 9, 9);
Rectangle cpy;
try {
// 인스턴스 복사
cpy = (Rectangle)org.clone(); // clone 호출 시 부모 타입의 객체를 가져오기 때문에 반드시 다운 캐스팅으로 데이터의 형을 맞춰주어야 합니다.
// 한 인스턴스의 좌표 정보를 수정
org.changePos(2, 2, 7, 7);
org.showPosition();
cpy.showPosition();
}
catch(CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
'자바 > 자바 입문 공부일지' 카테고리의 다른 글
자바 기초 공부 일지 32. BigInteger 클래스, BigDecimal 클래스, Math 클래스 (0) | 2022.11.01 |
---|---|
자바 기초 공부 일지 31. 래퍼 클래스, Number 클래스 (0) | 2022.11.01 |
자바 기초 공부 일지 29. 자바 가상머신의 메모리 할당 (0) | 2022.11.01 |
자바 기초 공부 일지 28. 예외의 처리 try~catch 구문 (0) | 2022.11.01 |
자바 기초 공부 일지 27. 추상 클래스 (0) | 2022.10.31 |