[Kotlin] data class 란?
1. Kotlin 의 data class 란?
데이터 보관을 목적으로 사용하는 클래스
프로퍼티에 대한 getter(), setter(), equals(), hashCode(), toString(), copy(), componentN() 메소드를 컴파일 시점에 자동으로 생성한다.
class 앞에 data 를 붙여 정의한다.
2. Kotlin 의 data class 를 사용하는 이유
코딩을 하다보면, data 를 보관하는것이 주 목적인 클래스를 만들어야 하는 상황이 있다.
그러한 클래스들에는 아래와 같이 거의 필수적으로 작성되는 기능들이 있다.
getters(), setters(), equals(), hashcode(), toString(), copy(), componentN()
이러한 기능들을 데이터 클래스를 만들때마다 반복해서 작성하는 것은 아래와 같이 boilerplate code 를 야기한다.
Kotlin 은 이렇게 data 를 hold 하기 위해 사용하는 클래스를 만들 때, 이러한 boilerplate code 를 컴파일러가 자동으로 작성할 수 있도록 하는 기능을 만들었다.
그것이 바로 data class 이다.
// getter 와 setter 를 작성한 Java POJO class 의 형태
public class Person {
private String name;
private int age;
public Person(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(String int) {
this.age = age;
}
}
// 위의 POJO 클래스와 같은 기능을 하지만, boilerplate code 가 사라졌다
data class person(
val name: String,
val age: Int
)
3. Kotlin data class 사용 방법
(1) data class 의 property 접근
data class Person(val name: String, val age: Int)
fun main(args: Array) {
val olaf = Person("올라프", 24)
val olafName = olaf.name // name 프로퍼티에 접근
val olafAge = olaf.age // age 프로퍼티에 접근
olaf.age = 25 // Error: Val cannot be assigned
}
위의 예시에서는 name 과 age 프로퍼티가 val 로 선언되어있기 때문에, 컴파일 시점에 getter 만 생성되고, setter 는 생성되지 않는다. 그래서 프로퍼티의 값을 수정하려고 하면 에러가 발생한다.
data class 의 프로퍼티가 꼭 val 일 필요는 없지만, 데이터 클래스의 모든 프로퍼티를 읽기 전용으로 만들어 데이터 클래스를 불변(immutable) 클래스로 만드는 것이 권장된다. 특히 HashMap 등의 컨테이너에 데이터 클래스 객체를 담는 경우에는 불변성이 필수적이다.
(2) data class 의 copy() 함수
data class Person(val name: String, val age: Int)
fun main(args: Array) {
val olaf = Person("올라프", 24)
// olaf 객체를 복사하여 나이를 업데이트한 olderOlaf 객체를 만들었다
// 기존의 olaf 객체는 변하지 않는다
val olderOlaf = olaf.copy(age = 25)
}
코틀린 컴파일러는 데이터 클래스의 인스턴스를 더 쉽게 불변 객체로 활용할 수 있게 메소드를 제공하는데, 이것이 copy() 함수이다. copy() 함수는 객체를 복사하면서 일부 프로퍼티를 바꿀 수 있게 해준다.
원본 객체를 메모리상에서 직접 바꾸는 대신 복사본을 만드는 것이 더 낫다고 한다. 복사본은 원본과 다른 생명주기를 가지며, 복사본의 프로퍼티 값을 바꾸거나 복사본을 제거해도 프로그램에서 원본을 참조하는 다른 부분에 전혀 영향을 주지 않기 때문이다.
(3) data class 의 toString(), hashCode, equals()
data class Person(val name: String, val age: Int)
fun main(args: Array) {
val olaf = Person("올라프", 24)
// toString() - 결과 : Person(name=올라프, age=24)
println(olaf.toString())
// hashCode() - 결과 : 1558187620
println(olaf.hashCode())
// equals() - 결과 : true
val olaf1 = Person("올라프", 24)
println(olaf.equals(olaf1))
// equals() - 결과 : false
val olaf2 = Person("짭라프", 25)
println(olaf.equals(olaf2))
}
- toString()
"ClassName(field1=value1, field2=value)" 형태의 String 으로 객체를 convert 하여 반환한다
- hashCode()
hashCode 란, 객체를 구별하는 정수값을 말한다. 동일한 값을 가졌다고 해서 동일 객체인것이 아니다. 동일 객체는 서로 같은 참조를 바라본다. 참고
JVM 언어에서는 equals() 가 true 를 반환하는 두 객체는 반드시 같은 hashCode() 를 반환해야한다는 제약이 있다.
- equals()
참조 동일성이 아닌 객체의 동등성을 검사한다.
kotlin 의 구조적 동등 연산자 (Structural equality operator) "==" 을 호출하여 사용할 수 있다. kotlin 에서의 "==" 연산자는 내부적으로 equals() 함수를 호출하는 방식으로 컴파일된다.
Java 에서 참조 동일성을 비교하는 "==" 연산자는 kotlin 에서는 "===" 연산자로 사용된다.
kotlin 의 is 검사는 Java 의 instanceOf 와 같다
(4) data class 의 데이터 분해 및 대입(Destructuring Declarations) : ComponentN()
data class Person(val name: String, val age: Int)
fun main(args: Array) {
val olaf = Person("올라프", 24)
// Destructuring Declaration 구문을 통해 객체를 여러 변수로 구조화
val (name, age) = olaf
// 실제 컴파일시에는 아래와 같이 해석된다
val name = olaf.component1()
val age = olaf.compenent2()
println(name) // 결과 - 올라프
println(age) // 결과 - 24
}
kotlin 은 데이터 클래스의 기본 생성자에 선언된 모든 property 에 해당하는 componentN() 함수를 생성한다.
componentN() 을 통해 Destructuring Declarations 를 사용할 수 있다. Destructuring Declarations 는 객체를 여러 변수로 구조화하기 위해 사용한다.
kotlinlang 공홈 - Destructuring Declarations : https://kotlinlang.org/docs/reference/multi-declarations.html
참고 사이트 >>>
kotlinlang 공홈 - data class : https://kotlinlang.org/docs/reference/data-classes.html
https://www.callicoder.com/kotlin-data-classes/
https://codechacha.com/ko/data-classes-in-kotlin/