728x90
깊은 복사 vs 얕은 복사
#Java
참고 : [Java] - 깊은 복사(Deep Copy) vs 얕은 복사(Shallow Copy)
깊은 복사 : ‘실제 값’을 새로운 메모리 공간에 복사하는 것
얕은 복사 : ‘주소 값’을 복사 → 참조하고 있는 실제값은 같다.
- Collection 은 clone() 이라는 메서드를 이용해서 얕은 복사를 만들 수 있다.
깊은 복사를 구현하는 방법은 여러가지
- Cloneable 인터페이스 구현
- 복사 생성자
- 복사 팩터리
- Cloneable 을 사용하지 말자.
Cloneable 인터페이스는 복제해도 되는 클래스임을 명시하는 용도의 믹스인 인터페이스지만, 아쉽게도 의도한 목적을 제대로 이루지 못했다. 여기서 큰 문제점은 clone 메서드가 선언된 곳이 Cloneable이 아닌 OBject이고, 그 마저도 protected이다. 그래서 Cloneable을 구현하는 것만으로는 외부 객체에서 clone 메소드를 호출할 수 없다. 리플렉션을 사용하면 가능하지만, 100% 성공하는 것도 아니다.
이러한 여러 문제점을 가진 인터페이스이지만, Cloneable 방식은 널리 쓰이고 있어서 잘 알아두는 것이 좋다.
Cloneable이 몰고 온 모든 문제를 되짚어봤을 때, 새로운 인터페이스를 만들 때는 절대 Cloneable을 확장해서는 안 되며, 새로운 클래스도 이를 구현해서는 안된다. final 클래스라면 Cloneable을 구현해도 위험이 크지는 않지만, 성능 최적화 관점에서 검토한 후 별다른 문제가 없을 때만 드물게 허용해야 한다.
기본 원칙은 '복제 기능은 생성자와 팩터리를 이용하는게 최고' 라는 것이다.
단, 배열만은 clone 메소드 방식이 가장 깔끔한, 이 규칙의 합당한 예외라 할 수 있다.
클래스
package study.querydsl.copyTest;
public class Member {
private String name;
private int age;
public Member() {
}
public Member(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
테스트
package study.querydsl.copyTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
class CopyObjectTest {
@Test
@DisplayName("얕은 복사는 주소값과 실제값 모두 같다.")
void 얕은복사() {
Member original = new Member("jyj", 29);
Member copy = original; // 얕은 복사
System.out.println("original.hashCode() = " + original.hashCode());
System.out.println("copy.hashCode() = " + copy.hashCode());
//2032891036
//2032891036
copy.setAge(20);
System.out.println("original = " + original.getAge());
System.out.println("copy = " + copy.getAge());
// 20
// 20
}
// 복사하는 과정을 따로 메서드로 만드는 것이 좋다.
@Test
@DisplayName("깊은 복사는 주소값이 다르고 실제값만 복사한다.")
void 깊은복사() {
Member original = new Member("jyj", 29);
Member copy = new Member(original.getName(), original.getAge());
System.out.println("original.hashCode() = " + original.hashCode());
System.out.println("copy.hashCode() = " + copy.hashCode());
//633240419
//116734858
copy.setAge(20);
System.out.println("original = " + original.getAge());
System.out.println("copy = " + copy.getAge());
//29
//20
}
@Test
@DisplayName("Collection 은 clone() 메서드를 통해 얕은복사 생성")
void List_복사() {
Member a = new Member("jyj", 29);
Member b = new Member("jyj", 30);
ArrayList<Member> list = new ArrayList<Member>();
list.add(a);
list.add(b);
Object cloneList = list.clone();
System.out.println("list.hashCode() = " + list.hashCode());
System.out.println("cloneList.hashCode() = " + cloneList.hashCode());
//949273941
//949273941
}
}
728x90
'Language > JAVA' 카테고리의 다른 글
Null , isEmpty , isBlank 비교 (0) | 2024.03.20 |
---|---|
Optional + orElse 와 orElseGet 메서드 비교 테스트 (0) | 2023.08.15 |
스트림의 중간, 최종연산 (0) | 2023.02.15 |
스트림(Stream) 의 특징 (0) | 2023.02.13 |
람다식과 함수형 인터페이스 (0) | 2023.02.13 |
댓글