Passer au contenu principal

📦 데이터 클래스

📖 데이터 클래스란?

**데이터 클래스(Data Class)**는 데이터를 보관하는 특별한 클래스입니다. 자동으로 유용한 메서드들을 생성해줍니다.

💡 기본 사용법

일반 클래스 vs 데이터 클래스

// 일반 클래스
class Person1(val name: String, val age: Int)

// 데이터 클래스
data class Person2(val name: String, val age: Int)

fun main() {
val p1 = Person1("홍길동", 25)
val p2 = Person2("홍길동", 25)

println(p1) // Person1@해시코드
println(p2) // Person2(name=홍길동, age=25) ← 자동 toString!
}

자동 생성 메서드

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

fun main() {
val person = Person("홍길동", 25)

// toString() - 자동 생성
println(person) // Person(name=홍길동, age=25)

// equals() - 내용 비교
val other = Person("홍길동", 25)
println(person == other) // true

// hashCode() - 해시 코드
println(person.hashCode())

// copy() - 복사
val copy = person.copy()
println(copy) // Person(name=홍길동, age=25)
}

🔧 copy 메서드

부분 수정

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

fun main() {
val person = Person("홍길동", 25, "hong@example.com")

// 나이만 변경
val older = person.copy(age = 30)
println(older)
// Person(name=홍길동, age=30, email=hong@example.com)

// 이메일만 변경
val newEmail = person.copy(email = "new@example.com")
println(newEmail)
}

불변성 유지

data class Point(val x: Int, val y: Int)

fun main() {
val point1 = Point(10, 20)

// 새로운 객체 생성 (원본 유지)
val point2 = point1.copy(x = 30)

println(point1) // Point(x=10, y=20) ← 변경 안 됨!
println(point2) // Point(x=30, y=20)
}

🎯 구조 분해

기본 구조 분해

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

fun main() {
val person = Person("홍길동", 25)

// 구조 분해 선언
val (name, age) = person

println("이름: $name") // 홍길동
println("나이: $age") // 25
}

일부만 사용

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

fun main() {
val person = Person("홍길동", 25, "hong@example.com")

// 필요한 것만
val (name, _) = person // _ 로 무시
println("이름: $name")

// 순서 중요!
val (_, age) = person
println("나이: $age")
}

리스트와 함께

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

fun main() {
val people = listOf(
Person("Alice", 25),
Person("Bob", 30),
Person("Charlie", 35)
)

for ((name, age) in people) {
println("$name: ${age}세")
}
// Alice: 25세
// Bob: 30세
// Charlie: 35세
}

🎯 실전 예제

좌표

data class Point(val x: Int, val y: Int) {
fun distance(other: Point): Double {
val dx = (x - other.x).toDouble()
val dy = (y - other.y).toDouble()
return kotlin.math.sqrt(dx * dx + dy * dy)
}
}

fun main() {
val p1 = Point(0, 0)
val p2 = Point(3, 4)

println("거리: %.2f".format(p1.distance(p2))) // 5.00

// copy로 이동
val p3 = p1.copy(x = 10)
println(p3) // Point(x=10, y=0)
}

사용자 정보

data class User(
val id: Int,
val username: String,
val email: String,
val isActive: Boolean = true
)

fun main() {
val user = User(
id = 1,
username = "hong",
email = "hong@example.com"
)

println(user)

// 비활성화
val deactivated = user.copy(isActive = false)
println("활성 상태: ${deactivated.isActive}")
}

상품

data class Product(
val id: String,
val name: String,
val price: Int,
val stock: Int
) {
fun sell(quantity: Int): Product? {
return if (stock >= quantity) {
copy(stock = stock - quantity)
} else {
null
}
}
}

fun main() {
var product = Product("P001", "노트북", 1500000, 10)
println(product)

// 3개 판매
product = product.sell(3) ?: product
println(product) // stock: 7

// 20개 판매 시도 (재고 부족)
val result = product.sell(20)
println(result) // null
}

API 응답

data class ApiResponse<T>(
val success: Boolean,
val data: T?,
val message: String = ""
)

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

fun main() {
// 성공
val success = ApiResponse(
success = true,
data = User(1, "홍길동")
)
println(success)

// 실패
val failure = ApiResponse<User>(
success = false,
data = null,
message = "사용자를 찾을 수 없습니다"
)
println(failure)
}

🔍 equals와 hashCode

동등성 비교

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

fun main() {
val p1 = Person("홍길동", 25)
val p2 = Person("홍길동", 25)
val p3 = Person("김철수", 25)

// 내용 비교 (자동 equals)
println(p1 == p2) // true
println(p1 == p3) // false

// 참조 비교
println(p1 === p2) // false (다른 객체)
}

Set과 Map에서 사용

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

fun main() {
val set = setOf(
Person("홍길동", 25),
Person("홍길동", 25), // 중복 제거됨
Person("김철수", 30)
)

println(set.size) // 2

val map = mapOf(
Person("홍길동", 25) to "학생",
Person("김철수", 30) to "선생님"
)

println(map[Person("홍길동", 25)]) // 학생
}

🤔 자주 묻는 질문

Q1. 언제 data class를 쓰나요?

A: 데이터만 보관할 때!

// ✅ 데이터만 보관 → data class
data class User(val id: Int, val name: String)

// ❌ 동작이 많으면 일반 class
class BankAccount(private var balance: Int) {
fun deposit(amount: Int) { ... }
fun withdraw(amount: Int) { ... }
}

Q2. var도 쓸 수 있나요?

A: 가능하지만 val 권장!

// 가능
data class Person(var name: String, var age: Int)

fun main() {
val person = Person("홍길동", 25)
person.age = 26 // 변경 가능

// 하지만 불변(val)이 더 안전!
data class ImmutablePerson(val name: String, val age: Int)
}

Q3. 상속할 수 있나요?

A: open class는 상속 가능, data class는 불가!

// ✅ 일반 클래스 상속 가능
open class Animal
data class Dog(val name: String) : Animal()

// ❌ data class는 상속 불가
data class Animal(val name: String)
// class Dog : Animal() // 오류!

Q4. componentN은 뭔가요?

A: 구조 분해의 내부 구현!

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

fun main() {
val person = Person("홍길동", 25)

// 구조 분해
val (name, age) = person

// 실제로는 이렇게 동작
val name2 = person.component1() // name
val age2 = person.component2() // age
}

🎬 마치며

데이터 클래스로 간편하게 데이터를 다루세요!

핵심 정리:
✅ data class로 자동 메서드 생성
✅ copy()로 불변성 유지
✅ 구조 분해로 간편한 접근
✅ equals/hashCode 자동 구현
✅ 데이터 보관용으로 사용

다음 단계: 상속에서 클래스 확장을 알아보세요!