Kotlin Koans Workshop - IV. properties
이미지 출처: https://kotlinlang.org/assets/images/open-graph/kotlin_250x250.png
테스트케이스로 코틀린 문법익히기
Chapter4 - Kotlin Property 이해하기
Kotlin Github repository의 kotlin-koans 프로젝트를 기반으로 포스팅 되었음을 밝힘니다.
출처: https://github.com/Kotlin/kotlin-koans
목차
custom setter 이용하기 목차 ⇈
src package
1
2
3
4
5
6
7
8
9
10
// propertyWithCounter property에 custom setter를 구현해서 호출 시 마다 counter가 1씩 증가되게 합니다.
// property field에는 넘겨받은 값을 할당합니다.
class PropertyExample() {
var counter = 0
var propertyWithCounter: Int? = null
set(v: Int?) {
field = v
counter++
}
}
test package
1
2
3
4
5
6
7
8
9
10
// 테스트케이스가 실행되면 propertyWithCounter가 3번 호출되므로 counter는 3이되고, 마지막에 할당한 값은 32이므로 property의 field값은 32가 됩니다.
@Test fun testPropertyWithCounter() {
val q = PropertyExample()
q.propertyWithCounter = 14
q.propertyWithCounter = 21
q.propertyWithCounter = 32
assertEquals("The property q.counter should contain the number of assignments to q.propertyWithCounter:", 3, q.counter)
// Here we have to use !! due to false smart cast impossible
assertEquals("The property q.propertyWithCounter should be set:", 32, q.propertyWithCounter!!)
}
custom getter 이용하기 목차 ⇈
src package
1
2
3
4
5
6
7
8
9
10
11
// 생성자에서 initializer에 람다식을 넘겨받도록 합니다.
// lazyValue property의 field값이 null인 경우 initializer()를 호출하여 함수 블럭을 실행하고 Int값을 넘겨받아 field값에 할당합니다.
class LazyProperty(val initializer: () -> Int) {
private val lazyValue: Int? = null
get() {
if (field == null) field = initializer()
return field
}
val lazy: Int
get() = lazyValue!!
}
test package
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test fun testLazy() {
var initialized = false
val lazyProperty = LazyProperty({ initialized = true; 42 })
assertFalse("Property shouldn't be initialized before access", initialized)
// lazyProperty.lazy 코드가 호출되고 lazyValue field값이 null이므로 생성자에 전달한 람다식이 실행됩니다.
val result: Int = lazyProperty.lazy
assertTrue("Property should be initialized after access", initialized)
assertEquals(42, result)
}
@Test fun initializedOnce() {
var initialized = 0
val lazyProperty = LazyProperty( { initialized++; 42 })
// lazyProperty.lazy 코드가 호출되고 lazyValue field값이 null이므로 생성자에 전달한 람다식이 실행됩니다.
lazyProperty.lazy
// lazyValue field값이 null이 아니므로 생성자에 전달한 람다식이 실행되지 않습니다.
lazyProperty.lazy
assertEquals("Lazy property should be initialized once", 1, initialized)
}
lazy function으로 초기화 위임하기1 목차 ⇈
src package
1
2
3
4
// lazy function을 이용하여 이전 예제에서 사용했던 custom getter를 대체할 수 있습니다.
class LazyPropertyUsingDelegates(val initializer: () -> Int) {
val lazyValue: Int by lazy(initializer)
}
test package
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test fun testLazy() {
var initialized = false
val lazyProperty = LazyPropertyUsingDelegates({ initialized = true; 42 })
assertFalse("Property shouldn't be initialized before access", initialized)
val result: Int = lazyProperty.lazyValue
assertTrue("Property should be initialized after access", initialized)
assertEquals(42, result)
}
@Test fun initializedOnce() {
var initialized = 0
val lazyProperty = LazyPropertyUsingDelegates( { initialized++; 42 })
lazyProperty.lazyValue
lazyProperty.lazyValue
assertEquals("Lazy property should be initialized once", 1, initialized)
}
lazy function으로 초기화 위임하기2 목차 ⇈
src package
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
class D {
var date by EffectiveDate()
// The property date$delegate of type EffectiveDate is created;
// the generated 'get' and 'set' accessors for 'date' are delegated to it.
// You can look at the bytecode (by calling "Show Kotlin Bytecode" action in IntelliJ IDEA) for details.
}
class EffectiveDate<R> : ReadWriteProperty<R, MyDate> {
var timeInMillis: Long? = null
operator override fun getValue(thisRef: R, property: KProperty<*>): MyDate = timeInMillis!!.toDate()
operator override fun setValue(thisRef: R, property: KProperty<*>, value: MyDate) { timeInMillis = value.toMillis() }
}
fun MyDate.toMillis(): Long {
val c = Calendar.getInstance()
c.set(year, month, dayOfMonth, 0, 0, 0)
c.set(Calendar.MILLISECOND, 0)
return c.timeInMillis
}
fun Long.toDate(): MyDate {
val c = Calendar.getInstance()
c.timeInMillis = this
return MyDate(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DATE))
}
test package
1
2
3
4
5
6
7
8
@Test fun testDate() {
val d = D()
/* Month numbering starts with 0 (0-Jan, 1-Feb, ... 11-Dec) */
d.date = MyDate(2014, 1, 13)
assertEquals(2014, d.date.year)
assertEquals(1, d.date.month)
assertEquals(13, d.date.dayOfMonth)
}