Data classes
데이터를 담는 것이 주 목적인 클래스.
data class User(val name: String, val age: Int)
인스턴스를 읽을 수 있게 출력하거나, 인스턴스끼리 비교하거나, 복사 등등의 기능을 수행해주는 멤버 함수들을 쓸 수 있다.
- .equals / .hashCode()
- .toString() 형식: "User(name=John, age=42)"
- .componentN() 선언된 순서대로 property들에 대응한다(해당한다?)
- .copy()
코드의 일관성과 의미있는 행동을 위해 데이터 클래스는 다음 요구사항을 모두 만족해야 한다.
- primary 생성자는 적어도 하나 이상의 매개변수를 가져야 한다.
- 모든 primary 생성자의 매개변수들은 val나 var로 마크되어 있어야 한다.
- 데이터 클래스는 추상 클래스, 상속 가능(? open) 클래스, sealed 클래스, 또는 inner 클래스가 될 수 없다.
데이터 클래스 멤버(아마 멤버함수)들의 생성은 멤버들의 상속에 대해 다음을 따라야 한다.
- equals(), hashCode(), 또는 toString()을 data class body 내에서 명시적으로 구현하거나 data class의 super class에서 final로 구현한 경우, 이러한 함수들은 생성되지 않고 구현된 것들이 사용된다.
- 만약 (data class의) supertype(상위 타입.. data class가 상속 받은 class..?)이 오버라이딩 가능하고 호환 가능한 타입들을 반환하는 componentN() 함수를 가지고 있다면 대응되는 함수들은 data class에 생성되고 supertype의 그 함수들을 오버라이딩한다. 만약 supertype의 함수들이 호환 불가능한 특성을 가지고 있거나 final 처리가 되어있기 때문에 오버라이딩이 불가하면 에러가 보고된다.
- componentN()과 copy() 함수들에 대한 명시적인 구현을 제공하는 것은 허용되지 않는다.
componentN()
더보기
val (name, age) = person
// 이러한 문법을 destructuring declaration이라고 부른다.
// 한 번에 여러개의 변수들을 생성할 수 있다.
println(name)
println(age)
// 위에서 선언된 name과 age는 독립적으로 사용할 수 있다.
// val (name, age) = person 을 컴파일하여 풀어보면 아래와 같다.
val name = person.component1() // person의 첫번째 property
val age = person.compoenent2() // person의 두번째 property
/*
person이 가진 property 수 N만큼 componentN()까지 호출 가능하다.
componentN() 함수들을 destructuring declaration에 쓰려면 operator 키워드로 마크해줘야 한다.
*/
// for 문에서도 사용된다.
// a는 component1()의 반환값을, b는 component2()의 반환값을 갖게 된다.
for ((a, b) in collection) { ... }
데이터 클래스는 다른 클래스들은 상속받을 수 있다.
JVM에서 생성된 클래스가 매개변수 없는 생성자를 가져야 한다면, property들에 대한 기본 값들이 정해져야 한다.
data class User(val name: String = "", val age: Int = 0)
Properties declared in the class body
컴퍼일러는 primary 생성자 안에 정의된 property들만을 자동으로 생성된 함수에 사용한다.
어떤 property를 생성된 함수들로부터 배제하고 싶다면, 그 property를 class body 안에 선언하면 된다.
data class Person(val name: String) {
var age: Int = 0
}
data class Person(val name: String) {
var age: Int = 0
}
fun main() {
val person1 = Person("John")
val person2 = Person("John")
person1.age = 10 // data class body에 선언된 property
person2.age = 20
println("person1 == person2: ${person1 == person2}")
// person1 == person2: true
// == 연산자는 .equals()와 같다.
// age라는 property는 data class body 내부에 선언된 property이기 때문에 == 연산에서 제외할 수 있다.
println("person1 with age ${person1.age}: ${person1}")
// person1 with age 10: Person(name=John)
// age는 data class body 내부에 선언되었으므로 .toString()에서 제외된다.
println("person2 with age ${person2.age}: ${person2}")
// person2 with age 20: Person(name=John)
// age는 data class body 내부에 선언되었으므로 .toString()에서 제외된다.
// age는 마찬가지로 .copy(), .hashCode(), .equals()에서 사용될 수 없다.
}
Copying
.copy() 함수를 사용하여 객체를 복사할 수 있고 몇몇 프로퍼티들의 값을 나머지 프로퍼티들의 변경 없이 대체할 수 있다.
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
// 바꾸고자 하는 프로퍼티명을 명시하여 값을 넣어준다.
Data classes and destructuring declaratioins
data classes에 대해 생성된 component functions들은 destructuring declarations에 사용하는 것이 가능하다.
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age")
// Jane, 35 years of age
Standard data classes
표준 라이브러리는 Pair와 Tirple이라는 클래스들을 제공한다. 그러나 이름이 붙여진 data classes이 더 좋은 설계라고 할 수 있는데, 왜나하면 data classes들은 프로퍼티들에 의미있는 이름을 붙임으로써 코드를 더 읽기 쉽게 만들기 때문이다.
'Kotlin 공부' 카테고리의 다른 글
kotlinlang.org 내가 보려고 정리 - Object expressions and declarations (0) | 2023.08.04 |
---|---|
kotlinlang.org 내가 보려고 정리 - Sealed classes interfaces (0) | 2023.08.03 |
kotlinlang.org 내가 보려고 정리 - Null safety (0) | 2023.08.01 |
kotlinlang.org 내가 보려고 정리 - Classes (0) | 2023.07.31 |
kotlinlang.org 내가 보려고 정리 - Functions (0) | 2023.07.28 |