티스토리 뷰

반응형
SMALL

클래스 중에 메소드 없이 필드로만 구성된 클래스가 있습니다. 자바를 포함한 객체 지향 프로그래밍에서 이러한 클래스를 Value Object Class라고 부릅니다.

class Test {
    String name;
    int age;
    String address;
}

 

Kotlin에서는 이런 Value Object Class를 더 편하게 이용할 수 있도록 data class라는 것을 제공하고 있습니다.

class 앞에 data라는 Keyword만 추가해주면 되는 것입니다. 아래 처럼 말이죠.

data class Good(val name: String, val age: Int)

이러한 data 클래스를 사용하려면 다음과 같은 제약 조건을 맞춰야 사용할 수 있습니다.

1. 주 생성자를 선언해야 하고 주 생성자의 Parameter는 최소 1개 이상이어야 한다.
2. 모든 주 생성자의 Parameter는 var, val 중 하나로 선언해야 한다.
3. abstract, open, sealed, inner 등의 예약어(Keyword)를 사용할 수 없다.

만약 위 조건들을 지키지 않는다면 어떻게 될까요? 위 조건들을 지키지 못하는 경우들을 몇 가지 살펴보도록 합시다.

// ERROR : Parameter가 없는 경우
data class Hello()

// ERROR : var, val을 사용하지 않는 경우
data class Person(name: String)

// ERROR : abstract 키워드를 사용한 경우
data abstract class Student(val studentId: Int)

// ERROR : var, val을 사용하지 않은 Parameter가 존재할 경우
data class House(val address: String, price: Int)

 

Data 클래스는 클래스에 정의된 데이터와 관련된 편리한 기능을 제공하고 있습니다.

하지만, 데이터 클래스에 선언된 모든 데이터가 아니라 주 생성자에 선언된 데이터에 한해서만 기능을 제공합니다.

그렇기 때문에 위에서 나온 3가지 조건이 생기게 된 것입니다.

 

데이터 클래스의 함수

데이터 클래스가 유용한 이유는 데이터 클래스에 선언한 데이터와 관련된 다양한 기능을 함수로 제공하기 때문입니다.

데이터 클래스는 사용자가 함수를 추가해주지 않아도 기본적으로 아래와 같은 메소드들을 기본적으로 제공합니다.

equals() / hashCode()
toString()
componentN()
copy()

equals()

이 메소드는 두 객체가 동일한 데이터를 가지고 있는지 판별할 수 있는 메소드입니다.

data class의 주 생성자에 선언된 변수에 한하여 두 data class 객체가 동일한 데이터를 가지고 있는지 판별할 수 있습니다.

class Product(val name: String, val price: Int)

data class User(val name: String, val age: Int)

fun main(args: Array<String>) {
    var product = Product("prod1", 100)
    var product1 = Product("prod1", 100)
    
    println(product.equals(product1))
    
    var user = User("Hello", 25)
    var user1 = User("Hello", 25)
    
    println(user.equals(user1))
}

결과는 어떨지 한 번 실행해보도록 합시다.

실행 결과 : 두 값이 다른 것을 알 수 있다.

Product라는 클래스는 일반 클래스이고 User라는 클래스는 Data 클래스입니다.

분명히 두 객체에 동일한 데이터를 넣어주었지만 실행 결과를 보면 다른 것을 알 수 있습니다. 왜 그럴까요?

 

일반 클래스에서 제공하는 equals() 메소드는 객체를 비교해주는 함수입니다. 객체가 갖고 있는 데이터를 비교해주는 메소드가 아니라 객체 자체를 비교하는 것이기 때문에 product와 product1는 분명 다른 객체이기에 false를 반환하게 됩니다.

반면, Data 클래스는 데이터를 비교하기 때문에 동일한 데이터를 갖고 있으므로 true를 반환하게 됩니다.

 

그러면 이번에는 색다른 것을 시도해보도록 합시다.

두 클래스 모두 Data 클래스이며 같은 데이터를 가지고 있습니다. 다만, 클래스의 이름이 다를 뿐입니다.

이럴 경우 어떻게 될 지 실행해보도록 합시다.

data class User(val name: String, val age: Int)

data class Person(val name: String, val age: Int)

fun main(args: Array<String>) {
    var user = User("Terry", 25)
    var person = Person("Terry", 25)
    println(user.equals(person))
}

두 클래스에 동일한 데이터를 가지고 있으니까 true를 반환해야 하겠죠?

이게 무슨 일 일까요? true가 나와야 하는데 false가 반환되었습니다.

객체가 다르면 데이터가 같아도 다른 것으로 간주한다는 것을 알 수 있습니다.

 

이번에는 주 생성자의 매개변수를 물론이고 클래스 안에 선언된 프로퍼티까지 비교를 할 지 알아보도록 하겠습니다.

data class User(val name: String, val age: Int) {
    var email: String = "test@test.com"
}

fun main(args: Array<String>) {
    val user = User("Terry", 25)
    val user1 = User("Terry", 25)
    
    user1.email = "good@good.com"
    
    println(user.equals(user1))
}

같은 이름과 나이를 갖는 두 객체가 있습니다. user1에 다른 이메일 값을 넣어보았습니다.

false가 아니라 true가 나온 것으로 보아 주생성자를 제외한 값은 비교의 대상에 포함되지 않는다는 것을 알 수 있습니다.

 

toString()

데이터 클래스에서 제공하는 toString() 메소드는 데이터 클래스의 데이터를 문자열로 반환하는 메소드입니다.

class Product(val name: String, val price: Int)

data class User(val name: String, val age: Int) {
    var email: String = "test@test.com"
}

fun main(args: Array<String>) {
    var product = Product("prod1", 100)
    println(product.toString())
    
    var user = User("Terry", 25)
    println(user.toString())
}

product는 일반 클래스이고 user는 데이터 클래스입니다. 일반 클래스를 toString으로 출력해보면 User는 주생성자의 프로퍼티 값을 출력하지만 Product는 출력해주지 못하는 것을 확인할 수 있습니다.

 

componentN()

데이터 클래스에는 componentN()이라는 메소드가 있는데 이 메소드의 사용 법은 component1(), component2() 등과 같이 사용할 수 있는 메소드입니다. 데이터 클래스의 주 생성자의 프로퍼티의 개수에 따라 사용할 수 있는 componentN()의 개수가 달라집니다. 

데이터 클래스의 주 생성자의 프로퍼티의 개수가 3개라면 component1() ~ component3() 까지 3개의 메소드가 자동으로 만들어 지며 사용할 수 있게 됩니다.

data class User(val name: String, val age: Int)

fun main(args: Array<String>) {
    var user = User("Terry", 25)
    println(user.component1())
    println(user.component2())
}

component1() 메소드는 주 생성자의 프로퍼티 중에서 첫 번째 값을 출력해주고 component2() 메소드는 주 생성자의 프로퍼티 중에서 두 번째 값을 출력해주게 된다.

 

componentN() 메소드가 그럼 어디에서 사용하는 것인가? 할 수 있는데 이 componentN() 메소드에서 제공하는 기능 중 유용한 기능중 하나가 분배 선언(Destructuring Declarations)라는 것인데 다음 처럼 데이터를 선언할 수 있습니다.

data class User(val name: String, val age: Int)

fun main(args: Array<String>) {
    var user = User(age = 25, name = "Terry")
    
    val (name, age) = user
    
    println("name: $name, age: $age")
}

val (name, age) = user 을 수행하게 되면 user의 첫 번째 값이 name에 들어가고 두 번째 값이 age에 들어 가게 됩니다.

 

만약 순서가 바뀌면 어떻게 될까요?

순서가 바뀌어도 주 생성자에 선언된 프로퍼티의 순서대로 값이 들어가게 됩니다.

 

copy()

이름에서 알 수 있듯이 데이터를 복사해주는 메소드입니다. 하지만 여기에 더해서 일부 값을 변경해서 복사할 수 있도록 지원합니다.

data class User(val name: String, val age: Int)

fun main(args: Array<String>) {
    var user = User(age = 30, name = "Terry")
    println(user.toString())
    
    var user2 = user.copy(name = "Hong")
    println(user2.toString())
}

별다른 설명이 필요하지는 않은 것 같습니다.

 

이번 포스트에서는 코틀린의 데이터 클래스에 대해 살펴보았습니다.

다음 포스트에서는 Enum 클래스에 대해 살펴보도록 하겠습니다.

 

 

반응형
LIST
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함