안녕하세요, 여행벌입니다.
오늘은 자바 Object 클래스의 clone 메소드에 대해서 정리해보겠습니다.
clone 메소드
Object 클래스에는 인스턴스의 복사를 위한 clone 메소드가 정의되어 있습니다.
1
|
protected Object clone() throws CloneNotSupportedException
|
cs |
이 메소드가 호출되면, 호출된 메소드가 속한 인스턴스의 복사본이 생성되고, 이렇게 만들어진 복사본의 참조 값이 반환됩니다.
단, Cloneable 인터페이스를 구현한 인스턴스를 대상으로만 위의 메소드를 호출할 수 있고, Cloneable 인터페이스를 구현하지 않은 클래스의 인스턴스를 대상으로 호출하면 CloneNotSupportedException 예외가 발생합니다.
Cloneable 인터페이스는 인스턴스를 복사해도 된다는 '마커 인터페이스'로 정의해야 할 메소드가 따로 존재하지 않는 인터페이스입니다.
저번 포스팅과 마찬가지로 좌표를 담는 클래스를 선언하고, clone 메소드를 이용해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package Hello;
class Point implements Cloneable{
private int xPos;
private int yPos;
public Point(int x, int y) {
xPos = x;
yPos = y;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
@Override
public boolean equals(Object obj) {
if(((Point)obj).xPos == xPos && ((Point)obj).yPos == yPos) return true;
return false;
}
}
public class test{
public static void main(String args[]) {
Point p1 = new Point(5, 7);
try {
Point p2 = (Point)p1.clone();
if(p1 == p2) System.out.println("참조 대상이 같습니다");
else System.out.println("참조 대상이 다릅니다");
if(p1.equals(p2)) System.out.println("내용이 같습니다");
else System.out.println("내용이 다릅니다");
}
catch(CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
|
cs |
clone 메소드를 이용하기 위해, Cloneable 인터페이스를 implements 한 것을 볼 수 있습니다.
clone 메소드는 새로운 인스턴스를 생성하고 참조 값을 반환해주므로, "==" 연산 결과는 서로 다르지만, equals 결과는 같다는 것을 예상할 수 있습니다.
1
2
3
|
[Output]
참조 대상이 다릅니다
내용이 같습니다
|
cs |
깊은 복사, 얕은 복사
clone 메소드를 이용하면 대상 인스턴스의 내용을 복사해서 새로운 인스턴스를 만들 수 있습니다.
그럼 다음과 같은 상황에서 clone 메소드는 어떻게 작용할까요?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
class Point implements Cloneable{
private int xPos;
private int yPos;
public Point(int x, int y) {
xPos = x;
yPos = y;
}
public void showPoint() {
System.out.println("좌표 : " + xPos + ' ' + yPos);
}
public void change(int x, int y) {
xPos = x;
yPos = y;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
@Override
public boolean equals(Object obj) {
if(((Point)obj).xPos == xPos && ((Point)obj).yPos == yPos) return true;
return false;
}
}
class Line implements Cloneable{
private Point p1;
private Point p2;
public Line(Point p1, Point p2) {
this.p1 = p1;
this.p2 = p2;
}
public void showPosition() {
System.out.println("Line안에 있는 Point들의 좌표");
System.out.println("p1의 좌표 ");
p1.showPoint();
System.out.println("p2의 좌표 ");
p2.showPoint();
}
public void change(int x1, int y1, int x2, int y2) {
p1.change(x1, y1);
p2.change(x2, y2);
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
@Override
public boolean equals(Object obj) {
if(p1.equals(((Line)obj).p1) && p2.equals(((Line)obj).p2)) return true;
return false;
}
}
|
cs |
Point 인스턴스를 2개 가지고 있는, Line 클래스를 정의했습니다. equals 메소드는 Point 의 내용들이 모두 같으면 true, 다르면 false 를 반환하도록 Point 클래스의 equals 메소드를 이용했습니다. showPosition, change 메소드는 인스턴스 변수의 값을 출력해주고, 값을 바꿔주는 메소드입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class test{
public static void main(String args[]) {
Line line1 = new Line(new Point(5, 7), new Point(3, 4));
Line line2;
try {
line2 = (Line)line1.clone();
line1.change(1, 2, 3, 4);
line1.showPosition();
line2.showPosition();
}
catch(CloneNotSupportedException e) {}
}
}
|
cs |
line1 인스턴스를 생성하고, line2는 line1 인스턴스를 clone 했습니다. 그 후, line1의 인스턴스 변수들의 값을 변경한 후, line1과 line2의 인스턴스 변수 값들을 출력해보았습니다.
1
2
3
4
5
6
7
8
9
10
11
12
|
[Output]
Line안에 있는 Point들의 좌표
p1의 좌표
좌표 : 1 2
p2의 좌표
좌표 : 3 4
Line안에 있는 Point들의 좌표
p1의 좌표
좌표 : 1 2
p2의 좌표
좌표 : 3 4
|
cs |
line1과 line2의 인스턴스 변수 값들이 동시에 변경된 것을 볼 수 있습니다.
우리가 원한 clone은 line 클래스 안에 있는 Point 인스턴스 변수 p1, p2 도 새롭게 인스턴스를 생성해서 위의 그림과 같이 각각 다른 Point 인스턴스를 참조하는 "깊은 복사" 를 원했습니다.
하지만, 결과를 보면 아래 그림과 같이 "얕은 복사"가 된 것을 확인 할 수 있습니다.
깊은 복사를 하기 위해서는 다음과 같이 clone 메소드를 수정해야 됩니다.
@Override
protected Object clone() throws CloneNotSupportedException{
Line copy = (Line)super.clone(); // Line을 먼저 copy
copy.p1 = (Point)p1.clone();
copy.p2 = (Point)p2.clone();
return copy;
}
Line 클래스의 인스턴스 변수인 p1, p2도 각각 다시 clone 메소드를 이용해 복사한다면, 우리가 원하는 깊은 복사를 할 수 있습니다. 결과는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
|
[Output]
Line안에 있는 Point들의 좌표
p1의 좌표
좌표 : 1 2
p2의 좌표
좌표 : 3 4
Line안에 있는 Point들의 좌표
p1의 좌표
좌표 : 5 7
p2의 좌표
좌표 : 3 4
|
cs |
깊은 복사가 잘 이루어져서, line1과 line2가 각각 완전히 다른 인스턴스들을 참조하고 있는 것을 확인할 수 있습니다.
배열의 clone 메소드 호출
배열도 인스턴스이므로, clone 메소드의 호출이 가능하도록 오버라이딩 되어있습니다. 하지만, 배열은 깊은 복사가 진행되도록 오버라이딩 되어 있지는 않습니다. 따라서 배열이 지니는 참조 값의 복사만 이루어질 뿐 해당 참조 값의 인스턴스까지는 복사되지 않습니다.
즉, 배열은 얕은 복사만 가능합니다.
'Computer Language > JAVA' 카테고리의 다른 글
[JAVA] #26 자바 Number 클래스 메소드 정리 (0) | 2020.06.23 |
---|---|
[JAVA] #25 자바 Wrapper 클래스 (0) | 2020.06.23 |
[JAVA] #23 자바 Object 클래스 equals 메소드 (0) | 2020.06.22 |
[JAVA] #22 자바 메모리 모델 (0) | 2020.06.19 |
[JAVA] #21 예외 클래스 (Throwable 클래스) (0) | 2020.06.18 |