[Kotlin] Data Class

Hood·2024년 12월 24일

Kotlin

목록 보기
9/18
post-thumbnail

✍ 코틀린과 친해지자

PS 문제를 하나씩 풀다 보니, 공부가 필요하다고 느낀 문법을 정리한 글입니다.


🔎 Data Class란?

Kotlin 공식 문서에 따르면 data class주로 데이터를 보관하기 위한 클래스입니다.
일반 클래스와 달리, 데이터를 다룰 때 자주 필요한 기능을 컴파일러가 자동으로 만들어준다는 특징이 있습니다.

예를 들어 객체를 비교하거나, 내용을 문자열로 출력하거나, 일부 값만 바꿔 복사하는 작업을 별도로 직접 작성하지 않아도 됩니다.

Kotlin에서 data class는 다음과 같이 선언합니다.

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

이렇게 선언하면 User는 단순히 값을 저장하는 클래스가 아니라,
데이터를 다루기 편한 여러 기능을 기본으로 가지는 클래스가 됩니다.


Data Class의 조건

data class를 사용하려면 몇 가지 조건을 만족해야 합니다.

  1. 주 생성자에 최소 한 개 이상의 매개변수가 있어야 합니다.
  2. 주 생성자의 모든 매개변수는 val 또는 var로 선언되어야 합니다.
  3. data classabstract, open, sealed, inner가 될 수 없습니다.

즉, data class는 상속 구조를 자유롭게 확장하기 위한 클래스라기보다,
데이터를 표현하는 목적에 집중된 클래스라고 이해하시면 됩니다.


Class vs Data Class

일반 클래스와 data class의 가장 큰 차이는
데이터를 다룰 때 필요한 기본 동작이 자동으로 제공되는지 여부입니다.

data class를 선언하면 컴파일러가 다음과 같은 함수를 자동으로 생성해줍니다.

  • equals()
  • hashCode()
  • toString()
  • copy()
  • componentN()

이 함수들은 모두 주 생성자에 선언된 프로퍼티를 기준으로 만들어집니다.
그래서 data class는 데이터를 저장하고 비교하는 용도로 특히 편리합니다.


toString()

data class 객체를 그대로 출력하면,
객체의 프로퍼티 이름과 값이 보기 좋은 형태로 자동 출력됩니다.

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

fun main() {
    val a = People("Smith", 20)
    println(a)
    // People(name=Smith, age=20)
}

일반 클래스에서는 객체를 출력했을 때 메모리 주소 형태로 보이는 경우가 많지만,
data classtoString()이 자동 생성되기 때문에 현재 담고 있는 값이 한눈에 보입니다.

디버깅하거나 로그를 확인할 때도 꽤 유용합니다.


copy()

copy()는 기존 객체를 바탕으로,
일부 값만 바꾼 새 객체를 만들 때 사용하는 함수입니다.

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

fun main() {
    val a = People("Smith", 20)
    val copyA = a.copy(age = 30)

    println(copyA)
    // People(name=Smith, age=30)
}

위 예제에서는 기존 객체 a의 값을 그대로 가져오되,
age30으로 변경한 새로운 객체를 만들고 있습니다.

즉, copy()를 사용하면 객체 전체를 다시 작성하지 않고도
원하는 값만 바꿔서 새 객체를 쉽게 만들 수 있습니다.


hashCode()

hashCode()는 객체를 해시 기반 컬렉션에서 다룰 때 사용되는 값입니다.
data class에서는 프로퍼티 값이 같으면 같은 hashCode()가 생성됩니다.

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

fun main() {
    val a = People("Smith", 20)
    val copyA = a.copy()

    println(a.hashCode())
    println(copyA.hashCode())
}

위처럼 같은 값을 가진 객체라면 hashCode()도 동일하게 나옵니다.
이 점은 Set, Map 같은 컬렉션에서 객체를 비교하거나 저장할 때 중요하게 작용합니다.

반면 일반 클래스는 별도로 오버라이드하지 않으면,
내용이 같더라도 다른 hashCode()를 가질 수 있습니다.


equals()

equals()는 두 객체의 내용이 같은지를 비교하는 함수입니다.

Kotlin에서는 보통 다음 두 가지 연산자를 함께 구분해서 봅니다.

  • == : 내용이 같은지 비교
  • === : 메모리상 같은 객체인지 비교
data class People(
    val name: String,
    val age: Int
)

fun main() {
    val a = People("Smith", 20)
    val b = People("Smith", 20)
    val c = a

    println(a == b)   // true
    println(a === b)  // false
    println(a === c)  // true
}

ab는 서로 다른 객체이지만,
프로퍼티 값이 같기 때문에 == 비교에서는 true가 나옵니다.

반면 ===는 같은 객체인지 확인하는 연산이기 때문에,
서로 다른 인스턴스인 abfalse가 됩니다.


componentN()

data classcomponentN() 함수도 자동 생성되기 때문에
구조 분해 선언을 사용할 수 있습니다.

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

fun main() {
    val a = People("Smith", 20)
    val (name, age) = a

    println("구조 분해 : $name, $age")
    // 구조 분해 : Smith, 20
}

위 코드에서 val (name, age) = a
a.component1(), a.component2()를 순서대로 호출한 것과 비슷하게 동작합니다.

즉, data class는 각 프로퍼티를 쉽게 꺼내 쓸 수 있어서
구조 분해가 필요한 상황에서도 편리합니다.


Data Class를 사용할 때 알아둘 점

한 가지 주의할 점은, 컴파일러가 자동 생성하는 함수들이
주 생성자에 선언된 프로퍼티만 기준으로 동작한다는 점입니다.

예를 들어 클래스 본문 안에 선언한 프로퍼티는
equals(), hashCode(), copy(), componentN() 생성 기준에 포함되지 않습니다.

data class User(val name: String) {
    var age: Int = 0
}

이 경우 name은 비교 기준에 포함되지만, age는 포함되지 않습니다.
그래서 data class를 설계할 때는 정말 데이터의 핵심이 되는 값들을 주 생성자에 넣는 것이 중요합니다.


📌 결론

이번 글에서는 Kotlin의 data class에 대해 정리해보았습니다.

data class는 단순히 값을 저장하는 클래스를 넘어서,
데이터를 다룰 때 자주 필요한 기능을 자동으로 제공해주는 편리한 문법입니다.

정리하면 다음과 같습니다.

  • data class는 데이터를 표현하기 위한 클래스입니다.
  • equals(), hashCode(), toString(), copy(), componentN()이 자동 생성됩니다.
  • 객체 비교, 복사, 출력, 구조 분해를 훨씬 간단하게 처리할 수 있습니다.
  • 자동 생성되는 기능은 주 생성자의 프로퍼티를 기준으로 동작합니다.

데이터가 두 개라면 Pair, 세 개라면 Triple로도 표현할 수 있지만,
의미 있는 이름을 가진 여러 값을 함께 다뤄야 한다면 data class를 사용하는 편이 더 읽기 좋고 관리하기도 편합니다.

참고 자료

  • Kotlin 공식 문서 Data classes
  • [Kotlin] 감동 실화 Data Class 알아보기
profile
달을 향해 쏴라, 빗나가도 별이 될 테니 👊

0개의 댓글