#데이터 클래스 (Data Class)
- 데이터를 저장하는 클래스를 사용할 때 유용한 클래스이다
- 'data class 클래스명 constructor(val name: String, val grade: Int)' 형태로 사용한다
[Kotlin Data Class의 기능]
코틀린의 Data Class는 toString(), equals(), hashCode() 를 컴파일러가 자동 생성 해준다
- toString() 오버라이드 : 클래스의 데이터 값을 문자열로 편하게 보고자
- equals() 오버라이드 : 동등 비교를 하고 싶을 경우
- hashCode() 오버라이드 : equals()오버라이드 하면 hashCode도 오버라이드 해야 함
Java나, 코틀린 일반 클래스를 사용한다면, 위의 3가지 함수를 일일이 오버라이딩 해서 사용해야 하는데,
일단 수동으로 어떻게/왜 오버라이딩 해서 사용하는 지, 동등 비교는 뭐고 동일 비교는 무엇인지 더 확인하고 싶은 경우
더보기를 통해...
- 동등 비교 : 두 객체의 주소값이 다르더라도 내부 데이터 값은 같은지 비교
- Java에선 equals
- Kotlin에선 == (내부적으로 equals 호출)
- 동일 비교 : 두 객체의 주소값 비교
- Java에선 ==
- Kotlin에선 ===
Java 최상위 객체인 Object의 equals는 원래 내부적으로 동일 비교로(==) 이루어져 있는데
우리가 지금까지 String 값비교를 equals 로 할 수 있었던 이유는
String 클래스의 equals가 Override 되어 있었기 때문이다(값 비교 결과를 return 하게끔)
아래 예제는 JAVA/KOTLIN에서 각각 equals 오버라이드 하지 않고 각각 동등/동일 비교를 진행 한 예제이다
동등, 동일 비교 모두 false가 반환 되는 것을 볼 수 있다
동등 비교에서 value값이 같을 경우 true를 반환하려면, equals를 오버라이드 해야 한다
JAVA
Student student1 = new Student("Tina", 20);
Student student2 = new Student("Tina", 20);
Log.i("test", "[Java] Student1 Student2 동등 비교 = " + student1.equals(student2)); //false
Log.i("test", "[Java] Student1 Student2 동일 비교 = " + (student1 == student2)); //false
KOTLIN
val student1 = Student("Tina", 20)
val student2 = Student("Tina", 20)
println("[Kotlin] Student1 Student2 동등 비교 = " + (student1 == student2)) // false -> 코틀린에서의 '==' 는 equals를 호출
println("[Kotlin] Student1 Student2 동일 비교 = " + (student1 === student2)) //false
Students 내부 데이터 값들이 같은 두 객체가 있고, 둘을 동등한 객체로 취급하고 싶을 때 우리는 equals를 오버라이드 한다 (아래와 같이)
class Student(var name: String, var age: Int) {
override fun equals(other: Any?): Boolean {
return if (other != null && other is Student) {
(other.age == this.age) && (other.name == this.name)
} else {
false
}
}
override fun hashCode(): Int {
return name.hashCode() * age * 130
}
}
*JVM에서는 equals() 메소드로 두 객체 비교시 equal 할 경우, hashCode도 두 객체는 동일한 integer를 반환해야 한다
Collection에서 객체 동등 비교 시, hashCode값을 먼저 비교하기 때문에 데이터 값이 동등하더라도 hashCode를 동일하게 맞추지 않으면 동등 비교가 제대로 이루어지지 않음 (hashCode = 객체를 식별할 수 있는 정수 값)
*Any.kt - hashCode()
If two objects are equal according to the equals() method, then calling the hashCode method on each of the two objects must produce the same integer result.
[Kotlin Data Class의 장점]
- 대부분의 데이터 클래스가 거의 동일한 형식으로 toString(), equals(), hashCode() 오버라이드를 찍어내듯 쓰고 있는데 이를 자동으로 컴파일러가 생성해주고
- equals만 override 하고 hashCode는 생략하는 등 잘못쓰이는 경우를 방지해주고
- 객체 copy 기능도 편리하게 사용 가능한(+ 데이터 일부만 변경해서 copy도 가능)...!!!
실제로 toString/equals/hashCode를 오버라이드 해준다는 것을 확인 해 보자
- toString() : 일반 클래스는 시 인스턴스의 Unique Id를 반환하고, Data 클래스는 데이터 값들을 보기 좋게 문자열로 반환한다
- equals() : 일반 클래스는 false를 반환하고 Data 클래스는 true를 반환한다
- hashCode() : 일반 클래스는 데이터가 같아도 인스턴스들의 hashCode가 다르지만 Data 클래스는 동일한 hashCode를 반환한다
data class StudentData constructor(val name: String, val grade: Int) //데이터 클래스
class StudentCommon(val name: String, val age: Int) //일반 클래스
fun printStudent() {
val common01 = StudentCommon("TinaCommon", 1)
val common02 = StudentCommon("TinaCommon", 1)
val data01 = StudentData("TinaData", 2)
val data02 = StudentData("TinaData", 2)
//1)toString 확인
println("common01 toString : ${common01.toString()}") //StudentCommon@2d98a335
println("data01 toString : ${data02.toString()}")//StudentData(name=TinaData, grade=2)
//2)equals 확인
println("common equals : " + (common01 == common02)) //false
println("data equals : " + (data01 == data02)) //true
//3)hashCode 확인
println("common01 hashcode : ${common01.hashCode()}") //764977973
println("common02 hashcode : ${common02.hashCode()}") //381259350
println("data01 hashcode : ${data01.hashCode()}") //-1506839696
println("data02 hashcode : ${data02.hashCode()}") //-1506839696
}
Copy 예제
data class Student(val name: String, val age: Int)
val studentTina = Student("Tina", 20)
val studentBang = studentTina.copy(name = "Bang")
println(studentTina) //Student(name=Tina, age=20)
println(studentBang) //Student(name=Bang, age=20)
# by 키워드
- 어떤 클래스를 상속하지 않고 확장/수정 하고 싶을 경우 사용하고 싶을 경우가 있는데, 그 방법으로는 보통 기존 클래스를 새로 확장/수정 하려는 클래스 내부에 필드로 가지고 있는 방법을 사용하곤 하는데...(아래와 같이)
interface Student {
val name: String
val age: Int
fun sleep()
fun eat()
fun goToSchool()
}
//기존 클래스 - final
class CommonStudent(override val name: String, override val age: Int) : Student {
override fun sleep() {
println("[${name}, ${age}] I sleep at 9pm")
}
override fun eat() {
println("[${name}, ${age}] I eat lunch in school cafeteria")
}
override fun goToSchool() {
println("[${name}, ${age}] I go to school everyday")
}
}
//새로운 클래스
//CommonStudent를 상속하지 않고 HighSchoolStudent 클래스를 만들어 동작을 추가 변경 하고 싶을 경우
class HighSchoolStudent(val commonStudent: CommonStudent) : Student {
override val name = commonStudent.name
override val age = commonStudent.age
//sleep 메소드만 동작을 다르게 구현하고 싶음
override fun sleep() {
println("[${commonStudent.name}, ${commonStudent.age}] I sleep at 12pm")
}
//위임
override fun eat() {
commonStudent.eat()
}
//위임
override fun goToSchool() {
commonStudent.goToSchool()
}
}
fun main() {
val highSchoolStudent = HighSchoolStudent(CommonStudent("Tina", 17))
highSchoolStudent.sleep()
highSchoolStudent.eat()
highSchoolStudent.goToSchool()
******결과******
[Tina, 17] I sleep at 12pm
[Tina, 17] I eat lunch in school cafeteria
[Tina, 17] I go to school everyday
}
문제점 - CommonStudent에서 수정/추가 하고 싶은 부분은 sleep() 메소드 하나임에도 불구하고 모든 함수/프로퍼티를 override 해야하는 불상사가 발생
대안 - 이 때!! 'by 키워드' 사용하면 컴파일러가 기존 클래스에 위임하는 메소드를 만들어 준다
동작 변경하고 싶으면 override로 재정의 해서 사용하면 된다
by 키워드를 사용한 HighSchollStudent 클래스
// Student 인터페이스에 대한 구현은 'commonStudent'에 맡기겠다.
// 어차피 Student 인터페이스에 대한 구현은 'commonStudent'에 돼있을 거고,
// HighSchoolStudent 클래스는 대부분의 메소드를 commonStudent 메소드를 그냥 호출 하기만 할 거니까
class HighSchoolStudent(val commonStudent: CommonStudent) : Student by commonStudent {
//재정의 하고 싶은 메소드만 override
override fun sleep() {
println("[${commonStudent.name}, ${commonStudent.age}] I sleep at 12pm")
}
}
1) 코드량 줄고 깔끔해 지며
2)'HighSchoolStudent' 생성 의도가 명확히 보여진다
출처 : Kotlin In Action - 에이콘 출판사
(위 도서를 학습하고 개인 학습용으로 정리한 내용입니다)
'Kotlin' 카테고리의 다른 글
[Kotlin] 4.람다 - 기본 (0) | 2021.11.15 |
---|---|
[Kotlin] 3. Object (0) | 2021.11.15 |
[Kotlin] 3.Interface, Class, 변경자, 초기화블록, 추상 프로퍼티 (0) | 2021.11.06 |
[Kotlin] 2. 코틀린 기본 - 함수(2) (0) | 2021.11.02 |
[Kotlin] 2. 코틀린 기본 - 함수(최상위/확장) (0) | 2021.10.31 |