var
또는 mutable
객체 사용val
을 사용해 읽기 전용 프로퍼티를 만들면 일반적인 방법으로는 값이 변하지 않는다.val list = mutableListOf(1, 2, 3)
val
프로퍼티는 값이 변경될 수 있긴 하지만 프로퍼티 레퍼런스 자체를 변경할 수 없어 동기화 문제를 줄일 수 있다.var
프로퍼티를 사용하는 val
프로퍼티는 var
프로퍼티가 변할 때 변할 수 있다.var name: String = "Marcin"
var surname: String = "Moskaja"
val fullname
get() = "$name $surname"
val
는 읽기 전용이지만, 불변을 의미하는 것은 아니다.
val
는 정의 옆에 상태가 바로 적히므로 코드 실행을 예측하는 것이 쉽고 스마트 캐스트 등의 추가 기능을 활용할 수 있다.Iterable
, Collection
, Set
, List
인터페이스는 읽기 전용 컬렉션MutableIterable
, MutableCollection
, MutableSet
, MutableList
인터페이스는 읽고 쓸 수 있는 컬렉션mutable
이 붙은 인터페이스는 읽기 전용 인터페이스를 상속 받아 변경을 위한 메서드를 추가한 것 뿐이다.Iterable<T>.map
과 .filter
등 함수는 ArrayList
를 리턴하는데 이는 변경 가능하다.val list = listOf(1, 2, 3)
if (list is MutableList) { // 이렇게 하지 말자
list.add(4)
}
mutable
로 변경해야 한다면 list.toMutableList
를 활용하면 된다.
Set
또는 Map
의 키로 사용할 수 있는데 mutable 객체는 그럴 수 없다.
Set
과 Map
은 처음 요소 값을 기반으로 버킷을 결정하기 때문에 요소에 수정이 일어나면 내부에서 요소를 찾을 수 없게 되버린다.Int
도 내부적으로 plus
와 minus
등이 자신을 수정한 새로운 Int
를 리턴한다.data
한정자를 사용하면 된다.
copy
메서드를 활용하면 모든 기본 생성자 프로퍼티가 같은 새로운 객체를 만들어 낼 수 있다.data class User(
val name: String,
val surname: String
)
var user = User("Maja", "Markiewicz")
user = user.copy(surname = "Moskaja")
val list1: MutableList<Int> = mutableListOf()
var list2: List<Int> = listOf()
// 두 가지 모두 변경 가능하다.
list1.add(1)
list2 = list2 + 1
list1
은 구체적인 리스트 구현 내부에 변경 가능 지점이 있다.
list2
는 프로퍼티 자체가 변경 가능 지점이다.
mutable
프로퍼티에 읽기 전용 컬렉션을 사용하는 것이 더 쉽다.
private
로 만들 수 있기 때문var announcements = listOf<Announcement>()
private set
mutable
객체를 외부에 노출하는 것은 굉장히 위험하다.
mutable
객체를 복제하는 방어적 복제를 활용하면 좋다.class UserHolder {
private val user: MutableUser()
fun get(): MutableUser = user.copy() // 방어적 복제
}
class UserRepository {
private val storedUsers: MutableMap<Int, String> =
mutableMapOf()
fun loadAll(): Map<Int, String> { // 업캐스팅
return storedUsers
}
}
var
보다는 val
를 사용하는 것이 좋다.mutable
프로퍼티/객체보다는 immutable
프로퍼티/객체를 사용하는 것이 좋다.immutable
데이터 클래스로 copy
를 활용하는 것이 좋다.mutable
컬렉션보다는 읽기 전용을 사용하는 것이 좋다.mutable
객체를 외부에 노출하지 않는 것이 좋다.