1. 사전지식
data class란 데이터의 보관이 목적인 클래스로 가장 큰 특징인 toString(), equals(), hashode(), copy()가 우리가 원하는 목적으로 오버라이딩이 되어 있어 데이터 출력, 비교, 복사가 편하다.
2. 의문점 및 해결
[의문점]
나는 객체(인스턴스)를 생성하고 이것을 '='을 통해 다른 변수에 할당해주면 얕은 복사가 된다는 것을 깨달았다.
나는 구글링을 해보면서 얕은복사가 아닌 깊은복사를 하기 위해선 data class의 copy()메서드, Cloneable 인터페이스 사용, Gson을 사용하면 된다고 했다.
나는 이 중에 data class의 copy()메서드에 대해 배워보겠다. 깊은 복사일까? 얕은 복사일까?
[해결]
2-1. data class의 생성자 매개변수가 기본자료형(Primitive)인 경우를 살펴보자.
/**
* 생성자 매개변수로 기본자료형(Primitive) 타입만 있는 경우
*/
data class primitiveAnimal(var name: String = "", var age: Int = 0)
fun main() {
/**
* 생성자 매개변수로 기본자료형(Primitive) 타입만 있는 경우
*/
val primitiveAnimalInstance = primitiveAnimal(name = "사자", age = 10)
println("$primitiveAnimalInstance ${primitiveAnimalInstance.hashCode()}") // 값을 바꾸기 전의 primitiveAnimalInstance의 값, 객체 고유의 해시코드 출력
val primitiveCopyAnimalInstance = primitiveAnimalInstance.copy()//원본 객체 복사
primitiveCopyAnimalInstance.name = "원숭이"
primitiveCopyAnimalInstance.age = 20
println("$primitiveAnimalInstance ${primitiveAnimalInstance.hashCode()}") // copy 함수로 primitiveAnimalInstance를 복사한 primitiveCompyAnimalInstance 값을 바꾸고 원본 인스턴스인 primitiveAnimalInstance의 값, 객체 고유의 해시코드 출력
println("$primitiveCopyAnimalInstance ${primitiveCopyAnimalInstance.hashCode()}") // primitiveCompyAnimalInstance 값, 객체 고유의 해시코드 출력
}
[결과값]
primitiveAnimal(name=사자, age=10) 48984102
primitiveAnimal(name=사자, age=10) 48984102
primitiveAnimal(name=원숭이, age=20) 1565595965
==> data class의 생성자 매개변수가 기본자료형(Primitive)인 경우의 결과값으로 미루어보아 완벽하게 깊은복사가 되는 것을 확인할 수 있다.
즉, copy()메서드를 이용하여 객체를 생성하게 되면 객체의 주소값을 토대로 생성되는 고유의 hashCode의 값도 새로 생성이 되는 것을 알 수도 있고 원본 인스턴스(객체)에도 영향을 안주는 결과가 보이기 때문이다.
2-2. data class의 생성자 매개변수가 참조자료형(reference)인 경우를 살펴보자.
/**
* 생성자 매개변수로 참조자료형(Reference) 타입이 있는 경우
*/
data class referenceAnimal(var name: String = "", val mutableList: MutableList<String>)
fun main() {
/**
* 생성자 매개변수로 참조자료형(Reference) 타입이 있는 경우
*/
val referenceAnimalInstance = referenceAnimal(name = "사자", mutableListOf("조류","포유류"))
println("$referenceAnimalInstance ${referenceAnimalInstance.hashCode()}") //원본 객체 복사
val referenceCopyAnimalInstance = referenceAnimalInstance.copy()
referenceCopyAnimalInstance.name = "원숭이"
referenceCopyAnimalInstance.mutableList.add("갑각류")
referenceCopyAnimalInstance.mutableList.remove("조류")
println("$referenceAnimalInstance ${referenceAnimalInstance.hashCode()}") // copy 함수로 referenceAnimalInstance를 복사한 referenceCopyAnimalInstance 값을 바꾸고 원본 인스턴스인 referenceAnimalInstance의 값, 객체 고유의 해시코드 출력
println("$referenceCopyAnimalInstance ${referenceCopyAnimalInstance.hashCode()}") // referenceCopyAnimalInstance 값, 객체 고유의 해시코드 출력
}
[결과값]
referenceAnimal(name=사자, mutableList=[조류, 포유류]) 153530649
referenceAnimal(name=사자, mutableList=[포유류, 갑각류]) 1759408769
referenceAnimal(name=원숭이, mutableList=[포유류, 갑각류]) -1018946674
==> 이상하다.
data class의 생성자 매개변수가 기본자료형(Primitive)인 경우의 결과값으로 미루어보아 완벽하게 깊은복사가 되는 것을 확인할 수 있었지만,
참조자료형이 포함이되는 경우는 깊은 복사가 되지 않았다. 왜냐하면 결과값을 보게되면,
copy()메서드를 통해 객체를 할당받은 변수에 대해서 기본자료형(Primitive)를 변경했을 경우는 원본객체가 변경 되지가 않았다.
하지만 참조자료형(Reference)를 변경했을 경우는 변경된 값에 따라 원본 객체의 값이 바뀌었고 hashCode조차 값이 변한 것을 알 수가 있다.
3. 결론
직접 코드로 결과값을 확인해보면서 data class의 copy()메서드는 부분적으로 깊은 복사이며, 얕은 복사라고 할 수 있었다.
==> 즉, 깊은 복사라고 해당하는 경우는 data class의 생성자 매개변수의 값들이 기본자료형(Primite)로만 구성이 되어 있어야 하며,
얕은 복사라고 해당하는 경우는 data class의 생성자 매개변수 중 한 개만이라도 참조자료형(Reference)의 타입을 가지고 있다면 그 참조자료형이 변경이 되는 순간 원본도 같이 변경이 되는 얕은 복사라고 할 수 있다.
*주의할점 : 참조자료형의 타입을 가지고 있더라도 참조자료형을 바꾸지 않고 기본자료형만을 바꾼다면 깊은 복사가 된다. 즉, 참조자료형이 포함되었을때 참조자료형의 값을 바꾼다면 그때부터 깊은복사가 얕은 복사로 변한다고 보면 된다.
[코드]
'Kotlin > 궁금한 점 의문 해결' 카테고리의 다른 글
얕은 복사를 이용해 문제를 풀었을때의 의문점 해결 (0) | 2023.03.13 |
---|---|
StringBuilder의 값 비교(==)와 주소값 비교(===)에 대한 의문점 해결 (0) | 2023.03.13 |