안드로이드 개발자 노트
[이펙티브 코틀린] Item16. 프로퍼티는 동작이 아니라 상태를 나타내야 한다. 본문
기본적으로 코틀린의 프로퍼티는 사용자 정의 게터와 세터를 가질 수 있습니다.
var name: String? = null
get() = field?.toUpperCase()
set(value) {
if (!value.isNullOrBlank()) {
field = value
}
}
이 코드에서 field라는 식별자를 확인할 수 있습니다.
이는 프로퍼티의 데이터를 저장해두는 백킹 필드(backing field)에 대한 레퍼런스입니다.
val을 사용해서 읽기 전용 프로퍼티를 만들 때는 field가 만들어지지 않습니다.
val fullName: String
get() = "$name $surname"
var를 이용한 프로퍼티는 게터와 세터를 정의할 수 있으며, 이를 파생 프로퍼티(derived property)라고 부릅니다.
이처럼 코틀린의 모든 프로퍼티는 디폴트로 캡슐화되어 있습니다.
프로퍼티를 통한 getter, setter접근은 필드와 다를 수 있습니다.
예를 들어 아래처럼 코드를 작성할 수도 있습니다.
var date: Date
get() = Date(millis) // 매번 Date 객체를 생성합니다.
set(value){
// 객체는 millis 필드만 가집니다.
millis = value.time
}
프로퍼티는 필드가 필요없으며, 오히려 프로퍼티는 개념적으로 접근자(getter/setter)를 나타냅니다.
이러한 원리로 코틀린은 인터페이스에도 프로퍼티를 정의할 수 있습니다.
interface Person {
val name: String
}
이는 게터를 가질 거라는 것을 나타내며, 다음과 같이 오버라이드할 수 있습니다.
open class SuperComputer {
open val theAnswer: Long = 42
}
class AppleComputer : SuperComputer() {
override val theAnswer: Long = 1_800_275_2273
}
마찬가지의 이유로 프로퍼티를 위임할 수도 있습니다.
val db: Database by lazy { connectDb() }
프로퍼티는 본질적으로 함수이므로, 확장 프로퍼티를 만들 수도 있습니다.
val Context.preferences: SharedPreferences
get() = PreferenceManager
.getDefaultSharedPreferences(this)
코드에서 확인할 수 있는 것처럼 프로퍼티는 필드에 접근할 수 있는 접근자를 나타낼 뿐입니다.
이처럼 프로퍼티를 함수 대신 사용할 수도 있지만, 이런 함수에 알고리즘의 동작을 나타내는 것은 좋지 않습니다.
// 가능은 하지만, 이렇게 쓰지 마세요!
val Tree<Int>.sum: Int
get() = when(this){
is Leaf -> value
is Node -> left.sum + right.sum
}
이러한 처리는 프로퍼티가 아니라 함수로 구현해야 합니다.
fun Tree<Int>.sum(): Int = when (this) {
is Leaf -> values
is Node -> left.sum() + right.sum()
}
원칙적으로 프로퍼티는 상태를 나타내거나 설정하기 위한 목적으로만 사용하는 것이 좋고, 다른 로직 등을 포함하지 않아야 합니다.
이러한 프로퍼티는 여러가지 오해와 잘못된 사용을 일으킬 수 있습니다.
구체적으로 프로퍼티 대신 함수를 사용하는 것이 좋은 경우를 정리해 보면, 다음과 같습니다.
- 비즈니스 로직을 포함하는 경우: 프로퍼티에 비즈니스 로직이 포함되어있으면, 객체가 어떻게 동작할지 예측하기 어렵습니다.
- 결정적이지 않은 경우: get의 멱등성이 깨질 수 있습니다. 함수를 여러번 호출하게되면, 반환값이 달라질 수 있습니다.
- 변환의 경우: 타입 변환의 경우에도, Int.toDouble() 같은 함수를 사용하지, 프로퍼티에서 변환을 처리할거라 예상하기 어렵습니다.
- 연산 비용이 높거나, 복잡도가 O(1)보다 큰 경우: 보통 프로퍼티getter, setter가 연산비용이 높다고 생각하지 않습니다. 이는 개발자의 최적화 대상에서 빠지게 됩니다.
- 게터에서 프로퍼티의 상태 변경이 일어나야 하는 경우: 관습적으로 getter, setter가 객체의 상태를 변화시킬거라고 생각하지 않습니다. 따라서 게터에서 프로퍼티의 상태 변화를 일으킨다면, 함수를 사용하는 것이 좋습니다.
많은 사람은 경험적으로, 프로퍼티는 상태 집합을 나타내고, 함수는 행동을 나타낸다고 생각합니다.
'Kotlin > 이펙티브 코틀린' 카테고리의 다른 글
[이펙티브 코틀린] Item17. 이름 있는 아규먼트를 사용하라 (0) | 2023.10.29 |
---|---|
[이펙티브 코틀린] Item15. 리시버를 명시적으로 참조하라 (0) | 2023.10.01 |
[이펙티브 코틀린] Item14. 변수 타입이 명확하지 않은 경우 확실하게 지정하라 (0) | 2023.10.01 |