객체를 복사하는 방법에는 여러 가지가 있다.
여기서는 얕은 복사, 방어 복사, 깊은 복사를 순차적으로 살펴보고,
이러한 방법들이 각각 어떻게 동작하는지, 어떻게 사용되는지를 알아보도록 한다.
자바에서는
=
연산자로 얕은 복사를 할 수 있다.
public class Name {
private final String value;
public Name(String value) {
this.value = value;
}
public static Name of(String value) {
return new Name(value);
}
public String getValue() {
return value;
}
}
public class Friend {
public Name name;
public Friend(Name name) {
this.name = name;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
}
public class Person {
private Name name;
private final List<Friend> friends;
public Person(Name name, List<Friend> friends) {
this.name = name;
this.friends = friends;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public List<Friend> getFriends() {
return friends;
}
}
public class Main {
public static void main(String[] args) {
Person personA = new Person(
Name.of("nameA"), Collections.emptyList());
Person personB = personA; // 얕은 복사 수행
System.out.println(personB == personA); // true
System.out.println("=====[BEFORE]=====");
System.out.println(personA.name().value()); // nameA
System.out.println(personB.name().value()); // nameA
personB.changeName(Name.of("nameB"));
System.out.println("=====[AFTER]=====");
System.out.println(personA.name().value()); // nameB
System.out.println(personB.name().value()); // nameB
}
}
public class Person {
private Name name;
private final List<Friend> friends;
public Person(Name name, List<Friend> friends) {
this.name = name;
this.friends = new ArrayList<>(friends); // 방어적 복사
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public List<Friend> friends() {
return new ArrayList<>(friends); // 방어적 복사
}
public void addFriend(Person person) {
this.friends.add(person);
}
}
public class Main {
public static void main(String[] args) {
List<Friend> friends = new ArrayList<>();
Person person = new Person(Name.of("nameA"), friends);
Friend friend1 = new Friend(Name.of("friend1"));
Friend friend2 = new Friend(Name.of("friend2"));
Friend friend3 = new Friend(Name.of("friend3"));
System.out.println("=====[BEFORE]=====");
System.out.println(
String.format("친구의 수: %d", person.friends().size()));
person.friends().forEach(t -> System.out.print(t.name().value() + " "));
// 친구의 수: 0
friends.addAll(List.of(friend1, friend2, friend3));
System.out.println("=====[AFTER]=====");
System.out.println(
String.format("친구의 수: %d", person.friends().size()));
person.friends().forEach(t -> System.out.print(t.name().value() + " "));
// 친구의 수: 0
}
}
방어 복사에서 복사본 자체는 원본과 다른 주소를 사용한다.
하지만 복사본 내부의 객체들은 원본 주소를 그대로 사용하므로 변경에 직접적인 영향이 생길 수 있다.
이 문제는 깊은 복사를 사용하여 해결할 수 있다.
public class Person {
private Name name;
private final List<Friend> friends;
public Person(Name name, List<Friend> friends) {
this.name = name;
this.friends = friends.stream()
.map(it -> new Friend(it.name()))
.collect(Collectors.toList()); // 깊은 복사
}
public Name getName() {
return name;
}
public List<Friend> friends() {
return friends.stream()
.map(it -> new Friend(it.name()))
.collect(Collectors.toList()); // 깊은 복사
}
}
public class Copy {
public static void main(String[] args) {
String[] folderA = {"my_computer", "my_document", "recycleBin"};
// 1. clone() 메서드 호출
System.out.println("=== 1. clone() 메서드 호출 ===");
String[] folderB = folderA.clone();
folderB[0] = "new_computer";
System.out.println("folderA = " + Arrays.toString(folderA));
System.out.println("folderB = " + Arrays.toString(folderB));
System.out.println();
// 2. System.arrayCopy() 메서드 호출
System.out.println("=== 2. System.arrayCopy() 메서드 호출 ===");
String[] folderC = new String[folderA.length];
System.arraycopy(folderA, 0, folderC, 0, folderA.length);
folderC[0] = "new_computer";
System.out.println("folderA = " + Arrays.toString(folderA));
System.out.println("folderC = " + Arrays.toString(folderC));
System.out.println();
// 3. Arrays.copyOf() 메서드 호출
System.out.println("=== 3. Arrays.copyOf() 메서드 호출 ===");
String[] folderD = Arrays.copyOf(folderA, folderA.length);
folderD[0] = "new_computer";
System.out.println("folderA = " + Arrays.toString(folderA));
System.out.println("folderD = " + Arrays.toString(folderD));
System.out.println();
// 4. Arrays.copyOfRange() 메서드 호출
System.out.println("=== 4. Arrays.copyOfRange() 메서드 호출 ===");
String[] folderE = Arrays.copyOfRange(folderA, 0, folderA.length);
folderE[0] = "new_computer";
System.out.println("folderA = " + Arrays.toString(folderA));
System.out.println("folderE = " + Arrays.toString(folderE));
System.out.println();
}
}
clone()
메서드
System.arraycopy()
메서드
Arrays.copyOf()
메서드
Arrays.copyOfRange()
메서드