기존에는 enum의 values()
를 통해 enum 값들을 가져왔다면 1.8.20에서 Experimental feature였던 기능인 entries
가 안정화 단계로 오면서 이제는 더 모던한 방법으로 enum값을 가져올 수 있다.
enum class TestEnum(val data: String){
A("123"),
B("456"),
C("789"),
D("1");
}
TestEnum.entries.find {it.data == "123"} // nullable, findOrNull 없음
Sealed class 와 같은 구조에서 사용가능한 상태를 관리할 때 편리하다
object로만 선언하는 경우 toString을 따로 구현해야하지만 data object로 선언하는 경우 그럴필요가 없다.
아래와 같은 상태 관리를 위한 sealed interface가 있을 때
sealed interface TestStatus {
object Loading: TestStatus
data class Success(val response: String): TestStatus
object Fail: TestStatus
}
각 상태를 호출해보면
TestStatus$Loading@5b464ce8
Success(response=Success Response)
TestStatus$Fail@17c68925
이와 같이 나오면서 상태값을 보고 판단하기에 조금 번거롭다.
object -> data object로 전환하면
sealed interface TestStatus {
data object Loading: TestStatus
data class Success(val response: String): TestStatus
data object Fail: TestStatus
}
Loading
Success(response=Success Response)
Fail
이와 같이 알아보기 편하게 나온다.
그리고 equals()
의 경우 data object는 싱글턴구조이지만 reflection등을 통해 추가로 만들어내는 경우도 있기에 ===
를 통한 레퍼런스 비교가아닌 ==
를 사용하는 것은 권장한다.
마지막으로 hashCode()
를 통해 만들어내는 해쉬값도 모두 동일하기에 참고!
value class에 대해서 먼저 정리하고 오자
함수의 인자로 혹은 클래스의 프로퍼티로 특정 값만 올 수 있도록 하려면 제일 간단한 방법은 wrapper class를 만들어서 사용하는 방법이다. 하지만 매번 불필요한 오버헤드가 발생하며 Primitive type이 아니라 최적화도 이루어지지 않는다. 이런 문제점을 하기 위해서 value class는 런타임 시 객체를 제거하고 해당 자리의 프로퍼티로 생성된다.
예를 들어 회원 정보를 생성할 때
data class UserInfo(
val id: String,
val email: EmailInfo,
val pw: String
)
@JvmInline
value class EmailInfo(val email: String){
init {
require(email.contains("@"))
}
fun getID() = email.split("@").firstOrNull()
fun getHost() = email.split("@").lastOrNull()
}
와 같이 이메일의 형식제한과 이메일의 앞 뒤를 가져오는 간단한 함수도 사용이 가능하다.
다른 예제로는 asyncAfter 함수의 실행 딜레이를 결정할 때 IOS에서는 .seconds()
, .minute()
와 같은 함수를 제공하는데 value class를 통해서도 동일하게 만들어서 가독성과 성능 모두 챙길 수 있다.
To be continue..
기존에는 value class에서는 public primary constructors만 생성이 가능했지만 이제는 private으로 생성 및 추가 생성자를 만들어서 사용이 가능하다.
@JvmInline
value class EmailInfo(private val email: String){
init {
require(email.contains("@"))
}
constructor(id: String, host: String): this("$id@$host") {
require(id.isNotBlank() && host.isNotBlank())
}
fun getID() = email.split("@").firstOrNull()
fun getHost() = email.split("@").lastOrNull()
}