일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 서비스 쓰레드 차이
- 안드로이드 os 구조
- 안드로이드 유닛테스트란
- 안드로이드 레트로핏 사용법
- Rxjava Observable
- rxjava disposable
- 멤버변수
- jvm이란
- ANR이란
- 플러터 설치 2022
- 안드로이드 유닛 테스트
- android ar 개발
- 안드로이드 유닛 테스트 예시
- android retrofit login
- 스택 자바 코드
- jvm 작동 원리
- 자바 다형성
- 안드로이드 레트로핏 crud
- 안드로이드 라이선스 종류
- 객체
- ar vr 차이
- 스택 큐 차이
- 2022 플러터 안드로이드 스튜디오
- 클래스
- 서비스 vs 쓰레드
- 안드로이드 라이선스
- 큐 자바 코드
- rxjava hot observable
- 2022 플러터 설치
- rxjava cold observable
- Today
- Total
나만을 위한 블로그
[Kotlin] val은 Immutable이 아니다? 본문
대부분 글들에선 var, val의 차이를 말할 때 val은 불변이고 읽기 전용이라고 말한다.
틀린 말은 아니다. 공식 문서에서도 val은 읽기 전용이라고 써 있다.
https://kotlinlang.org/docs/properties.html
(중략)...val 키워드를 써서 읽기 전용으로 선언할 수 있다
그러나 불변이라는 말은 없다. 그럼 val은 읽기 전용일 뿐이고 불변은 아닌 건가?
Dove Letter라는 곳에서 val이 정말 불변인지, 단순 읽기 전용인지 질문한 내용을 다룬 미디엄 포스팅이 있어서 이를 번역한다.
https://proandroiddev.com/the-val-property-immutable-in-kotlin-2e4cf49207d0
불변 프로퍼티의 이점이라고 하면 아래를 생각할 것이다.
- 예측 가능성, 안전성 : 불변 속성은 초기화하면 바꿀 수 없어서 동작을 쉽게 이해, 예측할 수 있어서 의도하지 않은 부작용의 위험이 줄어든다
- 사이드 이펙트 피하기 : 가변 속성은 예측할 수 없는 동작으로 이어질 수 있다. 불변성은 상태를 안정적으로 유지해서 버그 발생 가능성을 줄인다
- 함수형 프로그래밍 : 함수형 프로그래밍 등 최신 패러다임은 불변성을 강조해서 보다 모듈화되고 재사용 가능하며 유지관리 용이한 코드를 만들어 확장하기 쉽다
- 간소화된 상태 관리 : 불변성은 상태 관리를 최소화한다. 특히 컴포즈 같이 안정적이고 불변하는 객체는 불필요한 재구성을 줄여 성능을 향상시킨다
- 쓰레드 안전 : 불변 프로퍼티는 쓰레드에 안전하므로 복잡한 동기화가 없어도 경쟁 조건(race condition) 같은 동시성 문제를 방지한다
이제 코틀린의 프로퍼티에 불변성이 어떻게 적용되는지 확인한다.
변경 불가능 vs 읽기 전용
코틀린에선 var 키워드로 가변 프로퍼티를 선언하고 val 키워드로 바꿀 수 없게 재할당이 불가능하게 만든다.
하지만 val 프로퍼티는 정말 변경 불가능하게 되는가? 커뮤니티에 코틀린 프로퍼티의 변경 불가능성에 대해 어떻게 접근하는지 설문조사를 실시했다.
59%는 val이 읽기 속성이라고 생각하지만 41%는 불변이라고 생각하는 걸 볼 수 있다.
프로퍼티의 코틀린 문서를 보면 프로퍼티 선언 방법이 var, val 2개가 있다.
그러나 공식문서 어디에도 불변 또는 불변성에 대한 언급이 없다. 그냥 '읽기 전용'이라고만 언급한다. 이유는 2가지 명확한 차이점이 값을 읽기 전용으로 간주하지만 대부분의 경우 실제로는 불변이 아닌 이유를 설명한다.
객체는 계속 수정할 수 있다
val 프로퍼티가 선언된 후에도 참조하는 객체를 바꿀 수 있다.
val myList = mutableListOf("Item1", "Item2")
myList.add("Item3") // 리스트를 수정하지만 참조는 동일하게 유지됨
myList 참조는 재할당할 수 없지만 리스트 아이템은 계속 수정할 수 있다.
class Sample {
private val text: String = "title"
}
Sample 클래스를 디컴파일 후 코틀린 바이트코드를 보면 JVM 환경에서 최종적으로 어떻게 바뀌는지 볼 수 있다.
private final Ljava/lang/String; text
@Lorg/jetbrains/annotations/NotNull;() // invisible
val 프로퍼티는 바이트코드에서 final로 컴파일된다. 자바 언어 스펙에 따르면 이건 아래의 의미를 갖는다.
final 변수는 한 번 할당되면 항상 같은 값을 갖는다. final 변수가 객체에 대한 참조를 갖는 경우, 객체에 대한 연산에 의해 객체 상태가 바뀔 수 있지만 변수는 항상 같은 객체를 참조한다. 이건 배열에도 적용되는데 배열은 객체라서 final 변수가 배열에 대한 참조를 갖는 경우 배열에 대한 연산으로 배열의 구성요소가 바뀔 수 있지만, 변수는 항상 같은 배열을 참조한다
결국 val 키워드는 참조가 일정하다는 것만 보장할 뿐 객체의 불변성을 보장하지는 않는다.
클래스, 인터페이스는 여전히 프로퍼티를 재정의할 수 있다
클래스, 인터페이스가 여전히 프로퍼티를 재정의할 수 있어서 val 프로퍼티의 값이 바뀔 가능성이 있다는 뜻이다.
따라서 val은 모든 경우 엄격하게 불변이 아니다. 컴포즈의 State 인터페이스를 생각해 보라.
interface State<out T> {
val value: T
}
interface MutableState<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}
val state: State<String> = remember { mutableStateOf("text") }
(state as MutableState<String>).value = "changed"
val로 선언돼도 서브클래스가 이 프로퍼티를 재정의해서 잠재적으로 동작, 값을 바꿀 수 있다. 이런 유연성은 val이 참조에 대한 읽기 전용 접근을 보장하지만 특히 상속, 재정의가 관련된 경우 불변성을 완전히 보장하진 않는다는 걸 보여준다.
따라서 단순히 읽기 전용이라고 부르는 것보다 참조 불변성(참조는 바꿀 수 없음), 객체 가능성(객체 자체는 수정 가능)으로 설명하는 게 더 명확하게 구분할 수 있다.
그럼 객체를 정의할 때 그 객체는 정말 불변인가? 클래스가 정말 불변성을 가지려면 클래스는 읽기 전용 프로퍼티로만 구성돼야 하고 이런 프로퍼티는 원시 타입이거나 내부 상태 변경을 허용하지 않는 객체여야 하며 데이터 클래스로 선언해서 재정의되지 않게 해야 한다.
data class Sample(
val name: String,
val url: String,
val age: Int
)
데이터 클래스는 상속을 허용하지 않아서 불변 프로퍼티로만 구성된 경우 진정한 불변 객체로 간주할 수 있다.
바이트코드로 디컴파일하면 final 프로퍼티가 있는 final 클래스로 정의돼 수정, 확장할 수 없는 걸 볼 수 있다.
public final class com/skydoves/server/driven/core/model/Sample {
// access flags 0x12
private final Ljava/lang/String; name
@Lorg/jetbrains/annotations/NotNull;() // invisible
// access flags 0x12
private final Ljava/lang/String; url
@Lorg/jetbrains/annotations/NotNull;() // invisible
// access flags 0x12
private final I age
}
'개인 공부 > Kotlin' 카테고리의 다른 글
[Kotlin] 멀티 쓰레딩이란? synchronized란? (0) | 2023.11.09 |
---|---|
[Kotlin] 컬렉션 필터링 (filter, any, none, all) (0) | 2023.11.08 |
[Kotlin] KDoc 주석이란? (0) | 2023.09.19 |
[Kotlin] 함수형(SAM) 인터페이스란? (0) | 2023.09.19 |
[Kotlin] 에러 처리 Best Practices (0) | 2023.09.08 |