-
6-2. 특별한 클래스 사용하기 : 데이터 클래스(data class)Study(종료)/Kotlin 22.09.13 ~ 12.18 2022. 10. 22. 22:23
6장
6-1. 특별한 클래스 사용하기 : 이넘(enun) 클래스6-2. 특별한 클래스 사용하기 : 데이터 클래스 클래스
6-3. 특별한 클래스 사용하기 : 인라인 클래스(값 클래스)
6.2 데이터 클래스(data class)
1) 데이터 클래스와 데이터 클래스에 대한 연산
참조 타입의 값은 기본적으로 참조가 가리키는 객체의 정체성(identify)이 같으면 동일하다.
이 말은 두 객체가 같은 객체(메모리에서 같은 위치에 있는)라면 같다는 말이다.
이 경우 인스턴스의 필드 값(John, Deo, 25)는 고려 대상이 아니다.
class Person(val firstName: String, val lastName: String, val age: Int) fun main(){ val person1 = Person("John", "Doe", 25) val person2 = Person("John", "Doe", 25) val person3 = person1 println(person1 == person2) // false println(person1 == person3) // true }
클래스에 커스텀 동등성 비교가 필요하다면, equals () 메서드와 이와 연관된 hashCode() 메서드를 구현하는게 일반적이지만
데이터 클래스에 대해서는 코틀린이 클래스의 프로퍼티 목록을 기반으로 몇가지 메서드를 자동으로 생성한다.
data class Person(val firstName: String, val lastName: String, val age: Int) fun main(){ val person1 = Person("John", "Doe", 25) val person2 = Person("John", "Doe", 25) val person3 = person1 println(person1 == person2) // true println(person1 == person3) // true }
data class로 정의하면 컴파일러가 주생성자에 정의된 프로퍼티 값을 비교하는
동등성 비교 연산을 자동으로 생성해주므로 두 결과 모두 true를 반환한다.
프로퍼티의 값의 비교는 equals() 메서드를 이용한다.
따라서 깊은 동등성(deep equality) 비교 여부는 프로퍼티의 타입에 따라 달라진다.
data class Person(val firstName: String, val lastName: String, val age: Int) data class Mailbox(val address: String , val person: Person) fun main(){ val box1 = Mailbox("여의도", Person("John", "Doe", 25)) val box2 = Mailbox("여의도", Person("John", "Doe", 25)) println(box1 == box2) // true }
위 코드에서는 Person이 data class이므로 MailBox의 비교는 address 프로퍼티와 Person의 동등성 비교(box1의 Person의 프로퍼티와 box2의 프로퍼티를 비교)에 따라 결과가 결정된다.그래서 위 코드의 Person을 data class가 아닌 그냥 class로 비교하면 결과가 바뀌게 된다.
class Person(val firstName: String, val lastName: String, val age: Int) data class Mailbox(val address: String , val person: Person) fun main(){ val box1 = Mailbox("여의도", Person("John", "Doe", 25)) val box2 = Mailbox("여의도", Person("John", "Doe", 25)) println(box1 == box2) // false, 두 person이 다름 }
데이터 클래스는 toString() 메서드 구현도 생성해준다. 이 메서드는 클래스 인스턴스를 문자열로 변환해준다.
data class Person(val firstName: String, val lastName: String, val age: Int) fun main(){ val person = Person("John", "Doe", 25) println(person) // Person(firstName=John, lastName=Doe, age=25) }
주생성자의 파라미터에서 선언한 프로퍼티만 equals() / hashCode() / toString() 메서드 구현에 사용된다.
다른 프로퍼티들은 이 함수들의 생성에 영향을 미치지 못한다.
data class Person(val firstName: String, val lastName: String){ var age = 0 } fun main(){ val person1 = Person("John", "Doe").apply { age = 24 } val person2 = Person("John", "Doe").apply { age = 20 } println(person1) // Person(firstName=John, lastName=Doe) println(person2) // Person(firstName=John, lastName=Doe) println(person1 == person2) }
데이터 클래스의 copy() 함수는 인스턴스를 복사할 수 있으며, 복사하면서 몇몇 프로퍼티를 변경할 수 있다.인스턴스를 쉽게 복사할 수 있는 기능은 불변 데이터 구조를 더 쉽게 사용하도록 해준다.
var을 이용해 프로퍼티를 사용하기보다, val 을 이용하면 코드에 대한 추론이 쉬워지고 실수도 줄일 수 있다.
data class Person(val firstName: String, val lastName: String, val age: Int) fun Person.show() = println("$firstName $lastName: $age") fun main(){ val person = Person("John", "Doe", 25) person.show() // John Doe: 25 person.copy().show() // John Doe: 25 person.copy(lastName = "Smith").show() // John Smith: 25 person.copy(firstName = "Jane", age = 30).show() // Jane Doe: 30 }
2) 구조 분해 선언(Destructuring Declaration)
구조 분해 선언은 변수 이름을 하나만 사용하는 대신 괄호로 감싼 식별자 목록으로
여러 변수를 한꺼번에 정의할수 있게 해주는 일반화된 지역 변수 구문이다.
각 이름은 별도의 변수 선언과 같고 = 다음에 적은 데이터 클래스 인스턴스의 프로퍼티에 해당한다.
import kotlin.random.Random data class Person(val firstName: String, val lastName: String, val age: Int) fun newPerson() = Person(readLine()!!, readLine()!!, Random.nextInt(100)) fun main(){ val person = newPerson() val firstName = person.firstName val lastName = person.lastName val age = person.age if(age < 18) println("$firstName $lastName is under-age") }
위 코드를 구조 분해 선언을 통해 아래와 같이 변경할 수 있다.
fun main(){ val person = newPerson() val (firstName, lastName, age) = person // val firstName = person.firstName // val lastName = person.lastName // val age = person.age ... }
여기서 각 변수에 어떤 프로퍼티가 매핑되는지는 선언하는 변수의 이름에 의해 결정되는 것이 아닌
데이터 클래스의 생성자에 있는 각 프로퍼티의 위치에 따라 결정된다.
val (firstName, lastName, age) = Person("John", "Doe", 25) println("$firstName $lastName") // John Doe
val (lastName, firstName, age) = Person("John", "Doe", 25) println("$firstName $lastName") // Doe John
구조 분해 선언 전체는 타입이 없지만, 필요하면 각 컴포넌트 변수에 타입을 표기할 수 있다.
val (firstName: String, lastName, age) = Person("John", "Doe", 25)
구조 분해 선언에 데이터 클래스의 프로퍼티 수보다 적은 수의 변수가 들어갈 수 있다.
이경우 생성자 뒷부분에 선언된 프로퍼티는 추출되지 않는다.
val (firstName: String, lastName) = Person("John", "Doe", 25) println("$firstName $lastName") // John Doe val (name) = Person("John", "Doe", 25) println(name) // John
중간 프로퍼티를 생략하고 싶을 경우 _ 를 사용해 선언하면 된다.
val (_, name) = Person("John", "Doe", 25) println(name) // Deo
다음글
반응형'Study(종료) > Kotlin 22.09.13 ~ 12.18' 카테고리의 다른 글
7-1 컬렉션과 I/O 자세히 알아보기 : 컬렉션 타입(1) (0) 2022.10.28 6-3. 특별한 클래스 사용하기 : 인라인 클래스(값 클래스) (0) 2022.10.23 6-1. 특별한 클래스 사용하기 : 이넘(enun) 클래스 (0) 2022.10.13 5-5. 고급 함수와 함수형 프로그래밍 활용하기 : 수신 객체가 있는 호출 가능 참조 (0) 2022.10.10 5-4. 고급 함수와 함수형 프로그래밍 활용하기 : 동반 확장 (0) 2022.10.09