이 글의 내용은 [이펙티브 코틀린]을 참고합니다.
때로는 데이터를 한꺼번에 전달해야 할 때가 있습니다. 아래 코드처럼 말이죠.
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 사용
- 사용자가 데이터 클래스에 적혀 있는 것과 다른 이름을 활용해 변수를 해제하면, 경고가 출력됩니다.
이처럼 데이터 클래스를 활용하면 튜플을 활용할 때보다 더 많은 장점이 있습니다.
따라서 클래스를 활용하는데 두려움을 갖기 말고, 적극적으로 활용합시다.
'...' 카테고리의 다른 글
[K8S] EKSCTL 를 이용한 EKS 구성 (0) | 2022.08.03 |
---|---|
[AWS] S3 퍼블릭 액세스 차단을 활성화 하면서 객체에 접근하는 방법을 알아보자!! (0) | 2022.06.28 |
[Spring] Security +Google Oauth2 + JWT 구현하기 (4) - JWT 재발급 해주기 (0) | 2022.06.05 |
[Spring] Security +Google Oauth2 + JWT 구현하기 (3) - 생성한 JWT에 대해 인증/인가 하기 (0) | 2022.06.05 |
[Spring] Security +Google Oauth2 + JWT 구현하기 (2) - JWT에 권한 추가해주기 (0) | 2022.06.04 |