일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- android
- architecture
- livedata
- GitHub
- Coroutine
- AWS
- 코루틴
- 코드포스
- 백준
- 쿠링
- MyVoca
- activity
- TEST
- ProGuard
- Codeforces
- MiTweet
- relay
- Gradle
- Coroutines
- textfield
- Kotlin
- Compose
- Python
- androidStudio
- pandas
- 암호학
- boj
- Rxjava
- Hilt
- 프로그래머스
- Today
- Total
이동식 저장소
[Kotlin] Computed Property 본문
간단한 직사각형 클래스를 정의해 보자. 직사각형은 너비, 높이, 넓이 속성을 갖는다.
data class Rectangle(
val width: Int,
val height: Int,
val area: Int,
)
끝~
...일 리가 없지. 사실 이 구현은 매우 잘못된 구현이다. ``area``가 ``width``와 ``height``의 곱으로 주어진다는 보장이 없기 때문이다.
약간 개선해 보면, 생성자에서 ``width``와 ``height``를 입력받아 ``area``를 내부적으로 계산하게 정의할 수 있다.
data class Rectangle(
val width: Int,
val height: Int,
) {
val size: Int = width * height
}
``width``와 ``height``가 모두 ``val``이므로 값이 변할 염려도 없으니, 이제 ``size``가 항상 ``width``와 ``height``의 곱과 같다고 확신할 수 있다. 설령 ``copy`` 함수를 사용하더라도 괜찮다. ``copy`` 함수는 내부적으로 생성자를 호출하기 때문에 ``size``도 다시 계산된다.
``var``이라면?
그런데 ``width``와 ``height``가 ``var``이라면 어떨까?
data class Rectangle(
var width: Int,
var height: Int,
) {
val size: Int = width * height
}
``size``는 객체를 생성될 때 한 번만 계산되는데, ``width``와 ``height``가 ``var``이므로 객체가 만들어진 후에도 얼마든지 변경될 수 있다. 너비 또는 높이가 바뀔 때 ``size``도 같이 바뀌게 할 수는 없을까?
Computed Property
Computed Property를 사용하면 된다. Computed property는 (이름에서도 알 수 있듯이) 다른 값에서부터 계산되는 값으로, 계산하는 식은 custom getter에 정의하면 된다.
data class Rectangle(
var width: Int,
var height: Int,
) {
val size: Int
get() = width * height
}
``size`` 값이 참조될 때마다 custom getter가 실행된다. 따라서 ``width``와 ``height``가 다른 값으로 할당되더라도 ``size``의 값이 항상 올바르게 유지된다.
그런데 이렇게 하면 ``size``라는 이름의 함수를 정의한 것과 같지 않은가?
data class Rectangle(
var width: Int,
var height: Int,
) {
fun size() = width * height
}
같긴 한데
사실 내부적으로 custom getter는 자바의 getter 함수와 같은 역할을 한다. 따라서 custom getter와 ``size()`` 모두 내부적으로 함수 호출로 구현된다.
하지만 Kotlin을 사용하는 우리 입장에서 보면, 값에 접근하고 있으니 아무래도 함수보단 속성처럼 사용하는 편이 좋다.
// recommended
val size = rect1.size
// how about this?
val size = rect1.size()
사용 예시
내부적으로 ``MutableState``를 사용하는 ``abstract class AbstractViewModel``을 정의했다.
abstract class <S> AbstractViewModel(defaultState: S) {
private val mutableStates = MutableStateFlow(defaultState)
val states = mutableStates.asStateFlow()
}
이제 ``MainViewModel``을 구현해 보자.
class MainViewModel: AbstractViewModel(SomeState()) {
// ...
fun onNameUpdate(newName: String) {
mutableStates.value = mutableStates.value.copy(name = newName)
}
}
값을 업데이트하는 부분이 뭔가 어색하지 않은가? ``mutableStates``에 매번 접근하다 보니 코드가 너무 장황해진다.
Computed property와 custom getter/setter를 사용하면 더 간결한 코드를 작성할 수 있다. ``AbstractViewModel``을 수정하고, 바뀐 정의에 맞게 ``MainViewModel``을 업데이트하자.
abstract class <S> AbstractViewModel(defaultState: S) {
private val mutableStates = MutableStateFlow(defaultState)
private val states: StateFlow<S> = mutableStates.asStateFlow()
protected var state: S
get() = mutableStates.value
set(value) {
mutableStates.value = value
}
}
class MainViewModel: AbstractViewModel(SomeState()) {
// ...
fun onNameUpdate(newName: String) {
state = state.copy(name = newName)
}
}
훨씬 깔끔하지 않은가? ``state``의 구현 방법을 숨기는 동시에 값을 사용하기도 쉬워졌다. 좋은데? 내 코드도 당장 고치러 가야지
'Primary > Kotlin' 카테고리의 다른 글
[Kotlin] Coroutines Job (0) | 2022.11.10 |
---|---|
[Kotlin] Dispatcher (0) | 2022.11.08 |
[Kotlin] Coroutine 테스트 디버깅 후기 (0) | 2022.07.30 |
Kotlin Immutable Collections (0) | 2022.07.29 |
[Kotlin] sealed class vs. enum class (0) | 2022.07.07 |