본문 바로가기
개발/자바

[Java] clone()에 대해서

by hamcheeseburger 2021. 5. 17.

목차

- Shallow Copy과 Deep Copy란?

- Shallow Cloning

- Deep Cloning

- ( 번외 ) 배열의 Clone

 

1. Shallow Copy과 Deep Copy란?

Shallow Copy (얕은 복사) : 원본 객체를 복사하여 새롭게 생성하지만 원본 객체 내의 참조자료유형 변수는 원본 객체가 가지고 있는 객체 변수와 동일하다. (기본자료유형에 대해선 deepcopy가 이루어진다.)

Deep Copy (깊은 복사) : 원복 객체를 복사하는 것 뿐만 아니라, 원본 객체 내의 객체 변수들 까지도 완벽히 복사된다.

 

Shallow Copy 그림설명

cloned_diary라는 객체는 Diary객체의 복사본이지만, 그 내의 mydate객체에 대해선 같은 객체를 참조하고 있다.

 

Deep Copy 그림 설명

 

2. Shallow Copy

Java에서 객체에 대한 Clone() 메소드를 사용하려면 Clonable이라는 인터페이스를 구현해야 한다.

기본적으로 모든 객체의 부모 클래스인 Object의 clone()은 Shallow copy를 적용하고 있다.

 

즉, 아래 코드와 같이 super.clone()을 사용하면 Example객체의 clone()은 Shallow copy를 구현하는 것이다.

public class Example implements Cloneable{
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}
}

 

예제를 통해 shallow copy에 대해 자세히 알아보자.

MyDate Class

public class MyDate {
	private int year;
	private int month;
	private int date;
	
	public MyDate(int year, int month, int date) {
		super();
		this.year = year;
		this.month = month;
		this.date = date;
	}
	
    // getter, setter, toString 생략
}

Diary Class

public class Diary implements Cloneable{
	int index;
	String name;
	MyDate myDate;
	
	public Diary(int index, String name, MyDate myDate) {
		super();
		this.index = index;
		this.name = name;
		this.myDate = myDate;
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}
	
	// getter, setter, toString 생략
}

Main Class

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Diary diary1 = new Diary(1, "hamcheeseburger", new MyDate(2021, 5, 17));
		
		Diary diary2 = null;
		try {
			diary2 = (Diary) diary1.clone();
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("Original hashcode : " + diary1.hashCode());
		System.out.println("Copy hashcode : " + diary2.hashCode());
		
		System.out.println("Original MyDate hashcode : " + diary1.getMyDate().hashCode());
		System.out.println("Copy MyDate hashcode : " + diary2.getMyDate().hashCode());

	}

}

 

객체의 참조를 확인하기 위하여 hashCode() 를 사용하였다.

실행 결과가 어떻게 될까?

 

실행결과

Original hashcode : 1910163204
Copy hashcode : 305623748
Original MyDate hashcode : 758529971
Copy MyDate hashcode : 758529971

 

원본객체의 myDate 변수와 복사객체의 myDate 변수가 같은 hashCode를 갖는다는 것을 알게 되었다.

이 말은 즉, 원본객체와 복사객체가 갖는 myDate 변수가 같은 객체라는 것이다.

이는 myDate 변수가 참조자료유형이기 때문에 갖는 특성이다. 기본자료유형인 name, index 변수에 대해서는 deep copy가 이루어진다. 

 

 

hashCode의 비교만으로는 명확하게 와닿지 않으니 조금 다르게 적용해보자.

Main Class

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Diary diary1 = new Diary(1, "hamcheeseburger", new MyDate(2021, 5, 17));
		System.out.println("Original toString : " + diary1.toString());
		
		Diary diary2 = null;
		try {
			diary2 = (Diary) diary1.clone();
			diary2.setIndex(2);
			diary2.setName("Different Name");
			diary2.getMyDate().setMonth(10);
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("After Cloned Original toString : " + diary1.toString());
		System.out.println("Copy toString : " + diary2.toString());
	}

}

copy된 diary2 객체의 변수들을 각각 변경시켜 보았다. 실행결과가 어떻게 될까?

 

 

실행결과

Original toString : Diary [index=1, name=hamcheeseburger, myDate=MyDate [year=2021, month=5, date=17]]
After Cloned Original toString : Diary [index=1, name=hamcheeseburger, myDate=MyDate [year=2021, month=10, date=17]]
Copy toString : Diary [index=2, name=Different Name, myDate=MyDate [year=2021, month=10, date=17]]

diary2의 MyDate 객체에서 month를 변경 하였더니, 원본 객체인 diary1에서도 같은 값이 적용되었다.

같은 MyDate 객체를 공유하고 있음을 알 수 있다.

 

2. Deep Copy

위와 같은 예제에서 Diary 객체에 대한 Deep copy를 적용하려면 clone()에 코드를 더 추가해야만 한다.

public class Diary implements Cloneable{
	int index;
	String name;
	MyDate myDate;
	
	public Diary(int index, String name, MyDate myDate) {
		super();
		this.index = index;
		this.name = name;
		this.myDate = myDate;
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		Diary cloned = (Diary) super.clone();
		
		cloned.setMyDate(new MyDate(myDate.getYear(), myDate.getMonth(), myDate.getDate()));
		
		return cloned;
	}
    
    // getter, setter, toString 생략
}

super.clone()으로 shallow copy된 객체에 새로운 MyDate 객체를 생성하여 변수로 적용시켜주면 된다.

 

바로 위에서 실행했던 Main Class를 그대로 적용하여 실행시켜보자.

 

실행결과

Original toString : Diary [index=1, name=hamcheeseburger, myDate=MyDate [year=2021, month=5, date=17]]
After Cloned Original toString : Diary [index=1, name=hamcheeseburger, myDate=MyDate [year=2021, month=5, date=17]]
Copy toString : Diary [index=2, name=Different Name, myDate=MyDate [year=2021, month=10, date=17]]

 복사객체를 변경해도 원본객체에 아무런 영향을 주지 않는 것을 확인할 수 있다.

 

hashCode도 확인해보자.

실행결과

Original hashcode : 1910163204
Copy hashcode : 305623748
Original MyDate hashcode : 758529971
Copy MyDate hashcode : 2104457164

당연히 다른 객체들을 참조하기 때문에 서로 다른 hashCode 값이 나온다.

 

 

3. 번외

'배열의 Clone은 어떻게 이루어 질까?'

 

public static void main(String[] args) {
		// TODO Auto-generated method stub
		MyDate [] myDates = new MyDate[2];
		myDates[0] = new MyDate(2021, 5, 17);
		myDates[1] = new MyDate(1990, 3, 24);
		
		MyDate[] copyedMyDates = myDates.clone();
		
		System.out.println("Original hashcode : " + myDates.hashCode());
		System.out.println("Copy hashcode : " + copyedMyDates.hashCode());
		
		System.out.println("Original element hashcode : " + myDates[0].hashCode());
		System.out.println("Copy element hashcode : " + copyedMyDates[0].hashCode());
}

 

실행결과

Original hashcode : 971848845
Copy hashcode : 1910163204
Original element hashcode : 305623748
Copy element hashcode : 305623748

결론적으로 배열의 clone은 shallow copy이다. (ArrayList도 마찬가지다.) 배열도 결국엔 Object의 자식객체이기 때문이다. 배열 객체 자체는 새롭게 복사되지만 그 안의 요소들은 원본 배열의 요소들을 참조한다.

 

 

 

배열의 Deep copy를 구현하려면 아래와 같은 추가적인 코드가 필요하다.

public static void main(String[] args) {
	// TODO Auto-generated method stub
	MyDate [] myDates = new MyDate[2];
	myDates[0] = new MyDate(2021, 5, 17);
	myDates[1] = new MyDate(1990, 3, 24);
	
	MyDate[] copyedMyDates = myDates.clone();
	
	for(int i = 0; i < myDates.length; i++) {
		copyedMyDates[i] 
				= new MyDate(myDates[i].getYear(), myDates[i].getMonth(), myDates[i].getDate());
	}
		
	System.out.println("Original hashcode : " + myDates.hashCode());
	System.out.println("Copy hashcode : " + copyedMyDates.hashCode());
		
	System.out.println("Original element hashcode : " + myDates[0].hashCode());
	System.out.println("Copy element hashcode : " + copyedMyDates[0].hashCode());
}

 

실행결과

Original hashcode : 971848845
Copy hashcode : 1910163204
Original element hashcode : 305623748
Copy element hashcode : 758529971

'개발 > 자바' 카테고리의 다른 글

추상화란?  (2) 2022.03.17
compareTo()가 0을 반환하면 어떤 순서로 정렬되나?  (0) 2022.02.19
Java Stream.max() 사용법  (0) 2022.02.14
Comparable의 compareTo() 파헤치기  (0) 2022.02.14
JUnit 사용법  (0) 2022.02.11

이전 댓글