오늘은 Kotlin in Action에서 관례 부분에 대해 공부했다.
get
x[a, b] -----> x.get(a,b)
ex) mutableMap[key] -----> mutableMap.get(key)
set
x.[a, b] = c -----> x.set(a,b,c)
ex) mutableMap[key] = newValue -----> mutableMap[key].set(newValue)
in
in은 객체가 컬렉션에 들어있는지 검사한다. a가 c안에 속해있나? == c안에 a가 있나?
a in c ------> c.contains(a)
.. (rangeTo)
start..end -----> start.rangeTo(end)
구조 분해 선언은 일반 변수 선언과 비슷해보이지만, =의 좌변에 여러 변수들을 괄호로 묶었다는 점에서 다르다.
val p5 = Point(10,20)
val (x,y) = p5
println(x)
println(y)
val (a,b) = p ---> val a = p.component1() / val b = p.component2()
data 클래스의 주 생성자에 있는 프로퍼티에 대해서는 컴파일러가 자동으로 componentN 함수를 만들어준다.
data class NameComponents(val name: String, val extension: String)
fun splitFilename(fullName: String): NameComponents {
val (name, extension) = fullName.split(".", limit = 2) // 구조분해선언 사용
return NameComponents(name, extension)
}
//...
val fileName = "example.kt"
println(splitFilename(fileName)) //NameComponents(name=example, extension=kt)
}
맵의 원소
를 이터레이션할 때 유용하다.fun printEntries(map: Map<String, String>){
for ((key,value) in map){
println("$key -> $value")
}
}
// ...
val map = mapOf("K" to "KK", "A" to "AA")
printEntries(map)
/*
K -> KK
A -> AA
*/
Kotlin에서 클래스의 프로퍼티를 효과적으로 대리(delegate)하여 처리할 수 있는 기능
by
키워드를 통해 다른 객체에게 위임한다.class Foo {
var p: Type by Delegate()
}
class Delegate {
operator fun getValue(...) {...}
operator fun setValue(..., value: Type) {...}
}
여기서 Foo의 인스턴스 foo의 foo.p를 사용하면, 위임 프로퍼티 객체에 있는 메서드를 호출한다.
지연 초기화(lazy initialization)은 객체 일부분을 초기화하지 않고 남겨뒀다가 그 값이 필요할 경우 초기화할 때 사용하는 패턴이다.
class Person(val name: String) {
val emails by lazy { loadEmails(this) }
// loadEmails는 이메일을 DB에서 불러오는 임의의 함수
}
소스에서 변수가 최초로 이용되는 순간, 중괄호 부분이 자동으로 실행되어 결과값이 변수에 할당된다. 중괄호가 여러줄이면 마지막줄이 변수의 초기값이 된다.
lazy 함수는 코틀린 관례에 맞는 시그니처의 getValue 메서드가 있는 객체를 반환한다.
따라서 lazy를 by 키워드와 함께 사용해 위임 프로퍼티를 만들 수 있다.
lazy 함수의 인자는 값을 초기화할 때 호출할 람다다.
lazy 함수는 기본적으로 스레드 안전하다.
어떤 객체의 프로퍼티가 바뀔 때마다 리스너에게 변경 통지를 보내는 예시
by
키워드를 이용해 위임 객체를 지정하면 컴파일러가 작업을 자동으로 처리해준다.class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
var age: Int by ObservableProperty(age, changeSupport)
var salary: Int by ObservableProperty(salary, changeSupport)
}
object Users : idTable() { // 이 객체는 DB 테이블에 해당한다.
// 프로퍼티는 테이블 컬럼에 해당
val name = varchar("name", length = 50).index()
val age = integer("age")
}
class User(id: EntityId) : Entity(id) { // 각 User 인터페이스는 테이블에 들어있는 구체적 Entity에 해당
var name: String by Users.name // 사용자 이름은 DB의 "name" 컬럼에 있다.
var age: Int by Users.age
}
Users 객체는 DB 테이블이다. DB 전체에 하나만 존재하는 테이블이기 때문에 싱글톤으로 object 객체 선언했다.
User의 상위 클래스인 Entity 클래스는 DB 컬럼을 엔티티의 속성값으로 매핑해준다.
이 코드를 통해 User의 프로퍼티에 접근할 때 자동으로 Entity 클래스에 정의된 DB 매핑으로부터 필요한 값을 가져온다.