티스토리 뷰

이 글의 내용은 [이펙티브 코틀린]을 참고합니다.

 

때로는 데이터를 한꺼번에 전달해야 할 때가 있습니다. 아래 코드처럼 말이죠.

 

data class Player(
	val id: Int,
    val name: String,
    val points: Int
)

val player = Player(0, "Gecko", 9999)

 

data 한정자는 아래의 함수들을 자동으로 생성해줍니다.

  • toString
  • equals 와 hashCode
  • copy
  • componentN(component1, component2 등)

toString

클래스의 이름과 기본 생성자 형태로 모든 프로퍼티와 값을 출력 해줍니다. 주로 로그 출력이나 디버깅 할때 사용합니다.

println(player) // Player(id=0, name=Gecko, points=9999)

 

equals

기본 생성자의 프로퍼티가 같은지 확인해줍니다. hashCode 와 equals 는 같은 결과를 나타냅니다.

player == Player(0, "Gecko", 9999) // true
player == Player(0, "Ross", 9999) // false

 

Copy

immutable 데이터 클래스를 만들 때 편리합니다. copy는 기본 생성자 프로퍼티가 같은 새로운 객체를 복제합니다.

val newObj = player.copy(name = "Thor")
print(newObj) // Player(id=0, name=Thor, points=99999)

copy 메서드는 얕은 복사이지만 immutable 객체라면 상관이 없습니다. 어차피 프로퍼티 변경이 불가능할테니까요.

 

ComponentN 함수(Component1, Component2 등)

위치를 기반으로 객체를 해제할 수 있게 해줍니다.

val (id, name, pts) = player

이렇게 객체를 해제하는 코드를 작성하면, 코틀린은 내부적으로 componentN 함수를 사용하는 아래와 같은 코드로 변환합니다.

// 컴파일 후
val id: Int = player.component1()
val name: String = player.component2()
val pts: Int = player.component3()

이렇게 객체를 해제하면 장점과 단점이 있습니다.

 

장점

  • 변수의 이름을 원하는 대로 지정할 수 있습니다.
val visited = listOf("Korea", "Russia", "USA")
val (first, second, third) = visited
println("$first $second $third") // Korea Russia USA

val trip = mapOf(
	"Korea" to "Seoul",
	"Russia" to "moscow",
	"USA" to "Wasinton"
)

for ((country, city) in trip) {
	println("We Loved $city in $country")
}

 

단점

  • 위치를 잘못 지정하면 다양한 문제가 발생 할 수 있습니다.

 


튜플 대신 데이터 클래스 사용하기

데이터 클래스는 튜플보다 많은 것을 제공합니다.

 

튜플의 단점

  • 튜플은 데이터 클래스와 같은 역할을 하지만 훨씬 가독성이 떨어집니다.
  • 튜플만 보고 어떤 타입을 나타내는지 예측이 어렵습니다.

 

결국 튜플보다 데이터 클래스를 사용하는 것이 모든 면에서 좋기 때문에 튜플은 점차 없어지고 있습니다.

간단한 예입니다.

 

fun String.parseName(): Pair<String, String>? {
		val indexOfLastSpace = this.trim().lastIndexOf(' ')
		if(indexOfLastSpace < 0) return null
		val firstName = this.take(indexOfLastSpace)
		val lastName = this.drop(indexOfLastSpace)
		return Pair(firstName, lastName)
}

// 사용
val fullName = "Marcin Moskata"
val (fisrtName, lastName) = fullName.parseName() ?: return

 

위 코드의 문제점

  • Pair<String, String> 을 보고 전체 이름을 나타낸다는 것을 인지하기가 어렵습니다.
  • lastName과 firstName 중 어떤 것이 앞에 있을지 예측하기가 어렵습니다.

따라서 튜플보단 데이터 클래스를 활용하자!!

 

data class FullName(
		val firstName: String,
		val lastName: String
)

fun String.parseName(): FullName? {
		val indexOfLastSpace = this.trim().lastIndexOf(' ')
		if(indexOfLastSpace < 0) return null
		val firstName = this.take(indexOfLastSpace)
		val lastName = this.drop(indexOfLastSpace)
		return FullName(firstName, lastName)
}

// 사용
val fullName = "Marcin Moskata"
val (fisrtName, lastName) = fullName.parseName() ?: return

 

데이터 클래스를 사용했을 때의 장점

  • 함수의 리턴 타입이 명확해집니다. → Pair<String, String> 대신 FullName 사용
  • 사용자가 데이터 클래스에 적혀 있는 것과 다른 이름을 활용해 변수를 해제하면, 경고가 출력됩니다.

 

이처럼 데이터 클래스를 활용하면 튜플을 활용할 때보다 더 많은 장점이 있습니다.

따라서 클래스를 활용하는데 두려움을 갖기 말고, 적극적으로 활용합시다.

'Language > Kotlin' 카테고리의 다른 글

[Kotlin] 제네릭 타입과 variance 한정자를 활용하라  (2) 2022.04.29
댓글
댓글쓰기 폼
공지사항
Total
248,450
Today
825
Yesterday
1,065
링크
«   2022/10   »
            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          
글 보관함