들어가기 전

코틀린에서는 class와 data class가 존재합니다.

data class에 대해 설명을 하면서 일반적인 class와의 차이점을 다뤄보겠습니다.

 

data class

data class는 데이터 보관 목적인 클래스입니다.

 

data class의 형식은 아래와 같습니다.

 

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

 

data class는 보일러 플레이트 코드를 줄여줍니다. 보일러 플레이트 코드가 무엇인지 궁금한 분은 아래 블로그를 들어가시면 내용이 있으니 보고 이어서 내용을 보시면 좋을 거 같습니다.

 

보일러 플레이트란?

 

보일러 플레이트

들어가기 전 보일러 플레이트의 정의만 보고 "오? 보일러 플레이트코드 좋은 거 같은데 왜 보일러 플레이트코드를 줄이려고 하지?"라는 생각으로 처음에 이해가 잘 가지가 않았습니다. 그래서

hoestory.tistory.com

 

그리고 자바에서는 일반적으로 equals(), hashcode(), toString()을 최상위 객체인 Object 메서드를 재정의를 해서 사용이 가능합니다.

 

Java

@ToString
@EqualsAndHashCode
public class JavaExample {
    private String name;
    private int age;
}

 

그런데 코틀린에서는 아래 작성된 코틀린 코드처럼만 작성을 해도 equals(), hashcode(), toString(), copy() 메서드를 사용할 수 있습니다. copy() 메서드 같은 경우는 얕은 복사가 이루어집니다.

그리고 data class는  데이터 분해 및 대입(Destructuring Declarations)이 가능합니다.

 

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

 

toString()

fun main() {
    val dataClassExample = DataClassExample("칙칙이", 26)
    
    println(dataClassExample.toString()) //toString 생략 가능
    
}

 

결과

 

 

 

hashCode(), equals(), copy()

 

fun main() {
    val copyDataClassExample = dataClassExample.copy(name = "폭폭", 25)
    println("copyDataClassExample = " + copyDataClassExample)
    println("dataClassExample.hashCode() = " + dataClassExample.hashCode())
    println("copyDataClassExample.hashCode() = " + copyDataClassExample.hashCode())
    println("dataClassExample = " + dataClassExample.equals(copyDataClassExample))
}

 

결과

 

  • copy()를 통해 생성된 인스턴스는 얕은 복사가 이루어져서 복사본 데이터의 값을 변경하더라고 원본 객체의 데이터 값은 변하지 않습니다.
  • hashCode() 값을 보면 복사본 객체와 원본 객체의 hashCode() 값이 다르므로 다른 객체임을 나타냅니다.
  • equals()를 통해 원본 객체와 복사본 객체가 같은 값을 가진 것인지를 비교했을 때 false가 나옵니다. 이유는 copy()를 통해 인스턴스를 생성을 하면 새로운 인스턴스가 생성이 되기 때문에 복사본 객체와 원본 객체는 같다고 할 수 없습니다.

 

데이터 분해 및 대입(Destructuring Declarations)

data class에 정의한 프로퍼티 값들을 다른 변수에 분해해서 대입할 수 있습니다.

 

fun main() {
    val dataClassExample = DataClassExample("칙칙", 20)
    
    val sonName = dataClassExample.name
    val sonage = dataClassExample.age
}
  • getter()를 이용해서 값을 넣을 수 있습니다.

 

 

fun main() {

    val dataClassExample = DataClassExample("칙칙", 20)
    val (sonName, sonAge) = dataClassExample
}
  • DataClassExample에 값이 들어간 프로퍼티들을 분해해서 sonName, sonAge에 대입해 줍니다.
  • 대신 DataClassExample에 정의한 순서대로 변수를 선언을 해야 합니다. 

 

DataClassExample에 정의한 순서대로 변수를 선언한 결과

 

 

DataClassExample에 정의한 순서대로 변수를 선언하지 않은 결과

 

fun main() {

    val dataClassExample = DataClassExample("칙칙", 20)
    val (sonAge, sonName) = dataClassExample
}

 

 

데이터 분해 및 대입(Destructuring Declarations) 동작 원리

 

자동적으로 componentN()이라는 메서드가 생성이 됩니다.

 

data class DataClassExample(val name: String, val age: Int) {

    
    fun component1(): String {
        return this.name
    }

    fun component2(): Int {
        return this.age
    }
}

 

 

  • data class를 정의하면 component1, 2 등이 자동으로 생성이 됩니다.
  • 생성자에 선언한 프로퍼티 순으로 생성이 됩니다.
  • 데이터 분해 및 대입에서 생성자에 선언한 프로퍼티에 알맞게 변수를 선언을 하라고 설명드린 이유입니다.

※참고 : 위 예시 코드는 자동으로 생성해 주는 메서드를 이해하기 쉽게 명시한 코드입니다. 위처럼 클래스 내에 메서드를 정의하면 컴파일 에러가 발생합니다.

 

data class와 class의 차이점

우선 일반적인 class에서도 toString(), hashCode(), equals()를 사용할 수 있습니다.

그런데 data class처럼 값이 나오지 않고 인스턴스 주소값을 리턴합니다. 그리고 일반적인 class에서는 copy() 메서드를 사용할 수 없습니다.

차이점 정리

  • 일반적인 class에서 toString()은 인스턴스의 주소값을 리턴합니다.
  • 일반적인 class에서는 copy() 메서드를 사용할 수 없습니다.