Add library classes for delegated properties
This commit is contained in:
@@ -1,9 +1,8 @@
|
|||||||
package kotlin.properties
|
package kotlin.properties
|
||||||
|
|
||||||
import kotlin.*
|
|
||||||
import kotlin.util.*
|
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
import kotlin.properties.delegation.ObservableProperty
|
||||||
|
|
||||||
public class ChangeEvent(val source: Any, val name: String, val oldValue: Any?, val newValue: Any?) {
|
public class ChangeEvent(val source: Any, val name: String, val oldValue: Any?, val newValue: Any?) {
|
||||||
var propogationId: Any? = null
|
var propogationId: Any? = null
|
||||||
@@ -66,6 +65,10 @@ public abstract class ChangeSupport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun property<T>(init: T): ObservableProperty<T> {
|
||||||
|
return ObservableProperty(init) { name, oldValue, newValue -> changeProperty(name, oldValue, newValue) }
|
||||||
|
}
|
||||||
|
|
||||||
public fun onPropertyChange(fn: (ChangeEvent) -> Unit) {
|
public fun onPropertyChange(fn: (ChangeEvent) -> Unit) {
|
||||||
// TODO
|
// TODO
|
||||||
//addChangeListener(DelegateChangeListener(fn))
|
//addChangeListener(DelegateChangeListener(fn))
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package kotlin.properties.delegation
|
||||||
|
|
||||||
|
import kotlin.properties.ChangeSupport
|
||||||
|
|
||||||
|
public class NotNullVar<T: Any> {
|
||||||
|
private var value: T? = null
|
||||||
|
|
||||||
|
public fun get(thisRef: Any?, desc: PropertyMetadata): T {
|
||||||
|
return value ?: throw IllegalStateException("Property ${desc.name} should be initialized before get")
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun set(thisRef: Any?, desc: PropertyMetadata, value: T) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SynchronizedVar<T>(private val initValue: T, private val lock: Any) {
|
||||||
|
private var value: T = initValue
|
||||||
|
|
||||||
|
public fun get(thisRef: Any?, desc: PropertyMetadata): T {
|
||||||
|
return synchronized(lock) { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun set(thisRef: Any?, desc: PropertyMetadata, newValue: T) {
|
||||||
|
synchronized(lock) {
|
||||||
|
value = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObservableProperty<T>(initialValue: T, val changeSupport: (name: String, oldValue: T, newValue: T) -> Unit) {
|
||||||
|
private var value = initialValue
|
||||||
|
|
||||||
|
public fun get(thisRef: Any?, desc: PropertyMetadata): T {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun set(thisRef: Any?, desc: PropertyMetadata, newValue: T) {
|
||||||
|
changeSupport(desc.name, value, newValue)
|
||||||
|
value = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KeyMissingException(message: String): RuntimeException(message)
|
||||||
|
|
||||||
|
public abstract class MapVal<T, V> {
|
||||||
|
public abstract fun getMap(thisRef: T): Map<*, *>
|
||||||
|
public abstract fun getKey(desc: PropertyMetadata): Any?
|
||||||
|
|
||||||
|
public open fun getDefaultValue(desc: PropertyMetadata, key: Any?): V {
|
||||||
|
throw KeyMissingException("Key $key is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun get(thisRef: T, desc: PropertyMetadata): V {
|
||||||
|
val key = getKey(desc)
|
||||||
|
val map = getMap(thisRef)
|
||||||
|
if (!map.containsKey(key)) {
|
||||||
|
return getDefaultValue(desc, key)
|
||||||
|
}
|
||||||
|
return map[key] as V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class MapVar<T, V>: MapVal<T, V>() {
|
||||||
|
|
||||||
|
public fun set(thisRef: T, desc: PropertyMetadata, newValue: V) {
|
||||||
|
val map = getMap(thisRef) as MutableMap<Any?, Any?>
|
||||||
|
map[getKey(desc)] = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <V> Map<String, *>.readOnlyProperty(default: (() -> V)? = null): MapVal<Any?, V> {
|
||||||
|
return object: MapVal<Any?, V>() {
|
||||||
|
override fun getMap(thisRef: Any?) = this@readOnlyProperty
|
||||||
|
override fun getKey(desc: PropertyMetadata) = desc.name
|
||||||
|
override fun getDefaultValue(desc: PropertyMetadata, key: Any?) = if (default == null) super.getDefaultValue(desc, key) else default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <V> Map<*, *>.readOnlyProperty(default: (() -> V)? = null, key: Any?): MapVal<Any?, V> {
|
||||||
|
return object: MapVal<Any?, V>() {
|
||||||
|
override fun getMap(thisRef: Any?) = this@readOnlyProperty
|
||||||
|
override fun getKey(desc: PropertyMetadata) = key
|
||||||
|
override fun getDefaultValue(desc: PropertyMetadata, key: Any?) = if (default == null) super.getDefaultValue(desc, key) else default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <V> Map<*, *>.readOnlyProperty(default: (() -> V)? = null, key: (desc: PropertyMetadata) -> Any?): MapVal<Any?, V> {
|
||||||
|
return object: MapVal<Any?, V>() {
|
||||||
|
override fun getMap(thisRef: Any?) = this@readOnlyProperty
|
||||||
|
override fun getKey(desc: PropertyMetadata) = key(desc)
|
||||||
|
override fun getDefaultValue(desc: PropertyMetadata, key: Any?) = if (default == null) super.getDefaultValue(desc, key) else default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <V> MutableMap<String, *>.property(default: (() -> V)? = null): MapVar<Any?, V> {
|
||||||
|
return object: MapVar<Any?, V>() {
|
||||||
|
override fun getMap(thisRef: Any?) = this@property
|
||||||
|
override fun getKey(desc: PropertyMetadata) = desc.name
|
||||||
|
override fun getDefaultValue(desc: PropertyMetadata, key: Any?) = if (default == null) super.getDefaultValue(desc, key) else default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <V> MutableMap<*, *>.property(default: (() -> V)? = null, key: Any?): MapVar<Any?, V> {
|
||||||
|
return object: MapVar<Any?, V>() {
|
||||||
|
override fun getMap(thisRef: Any?) = this@property
|
||||||
|
override fun getKey(desc: PropertyMetadata) = key
|
||||||
|
override fun getDefaultValue(desc: PropertyMetadata, key: Any?) = if (default == null) super.getDefaultValue(desc, key) else default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <V> MutableMap<*, *>.property(default: (() -> V)? = null, key: (desc: PropertyMetadata) -> Any?): MapVar<Any?, V> {
|
||||||
|
return object: MapVar<Any?, V>() {
|
||||||
|
override fun getMap(thisRef: Any?) = this@property
|
||||||
|
override fun getKey(desc: PropertyMetadata) = key(desc)
|
||||||
|
override fun getDefaultValue(desc: PropertyMetadata, key: Any?) = if (default == null) super.getDefaultValue(desc, key) else default()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package kotlin.properties.delegation.lazy
|
||||||
|
|
||||||
|
private val NULL_VALUE: Any = Any()
|
||||||
|
|
||||||
|
private fun <T> escape(value: T): Any {
|
||||||
|
return if (value == null) NULL_VALUE else value
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T: Any> unescape(value: Any): T? {
|
||||||
|
return if (value == NULL_VALUE) null else value as T
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class LazyVal<T: Any>(initializer: () -> T): NullableLazyVal<T>(initializer) {
|
||||||
|
override fun get(thisRef: Any?, desc: PropertyMetadata): T {
|
||||||
|
return super.get(thisRef, desc)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class NullableLazyVal<T: Any>(private val initializer: () -> T?) {
|
||||||
|
private var value: Any? = null
|
||||||
|
|
||||||
|
public open fun get(thisRef: Any?, desc: PropertyMetadata): T? {
|
||||||
|
if (value == null) {
|
||||||
|
value = escape(initializer())
|
||||||
|
}
|
||||||
|
return unescape(value!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class VolatileLazyVal<T: Any>(initializer: () -> T): VolatileNullableLazyVal<T>(initializer) {
|
||||||
|
override fun get(thisRef: Any?, desc: PropertyMetadata): T {
|
||||||
|
return super.get(thisRef, desc)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class VolatileNullableLazyVal<T: Any>(private val initializer: () -> T?) {
|
||||||
|
private volatile var value: Any? = null
|
||||||
|
|
||||||
|
public open fun get(thisRef: Any?, desc: PropertyMetadata): T? {
|
||||||
|
if (value == null) {
|
||||||
|
value = escape(initializer())
|
||||||
|
}
|
||||||
|
return unescape(value!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class AtomicLazyVal<T: Any>(lock: Any? = null, initializer: () -> T): AtomicNullableLazyVal<T>(lock, initializer) {
|
||||||
|
override fun get(thisRef: Any?, desc: PropertyMetadata): T {
|
||||||
|
return super.get(thisRef, desc)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class AtomicNullableLazyVal<T: Any>(lock: Any? = null, private val initializer: () -> T?) {
|
||||||
|
private val lock = lock ?: this
|
||||||
|
private volatile var value: Any? = null
|
||||||
|
|
||||||
|
public open fun get(thisRef: Any?, desc: PropertyMetadata): T? {
|
||||||
|
val _v1 = value
|
||||||
|
if (_v1 != null) {
|
||||||
|
return unescape(_v1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return synchronized(lock) {
|
||||||
|
val _v2 = value
|
||||||
|
if (_v2 != null) {
|
||||||
|
unescape<T>(_v2)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val typedValue = initializer()
|
||||||
|
value = escape(typedValue)
|
||||||
|
typedValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package test.properties.delegation
|
||||||
|
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import kotlin.test.*
|
||||||
|
import kotlin.properties.*
|
||||||
|
import kotlin.properties.delegation.*
|
||||||
|
import kotlin.properties.delegation.lazy.*
|
||||||
|
|
||||||
|
trait WithBox {
|
||||||
|
fun box(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class DelegationTestBase: TestCase() {
|
||||||
|
fun doTest(klass: WithBox) {
|
||||||
|
assertEquals("OK", klass.box())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DelegationTest(): DelegationTestBase() {
|
||||||
|
fun testNotNullVar() {
|
||||||
|
doTest(TestNotNullVar("a", "b"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testObservableProperty() {
|
||||||
|
doTest(TestObservableProperty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestNotNullVar<T>(val a1: String, val b1: T): WithBox {
|
||||||
|
var a: String by NotNullVar<String>()
|
||||||
|
var b by NotNullVar<T>()
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
a = a1
|
||||||
|
b = b1
|
||||||
|
if (a != "a") return "fail: a shouuld be a, but was $a"
|
||||||
|
if (b != "b") return "fail: b should be b, but was $b"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestObservableProperty: WithBox, ChangeSupport() {
|
||||||
|
|
||||||
|
var b by property(init = 2)
|
||||||
|
var c by property(3)
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
var result = false
|
||||||
|
addChangeListener("b", object: ChangeListener {
|
||||||
|
public override fun onPropertyChange(event: ChangeEvent) {
|
||||||
|
result = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
addChangeListener("c", object: ChangeListener {
|
||||||
|
public override fun onPropertyChange(event: ChangeEvent) {
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
b = 4
|
||||||
|
if (b != 4) return "fail: b != 4"
|
||||||
|
if (!result) return "fail: result should be true"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
package test.properties.delegation
|
||||||
|
|
||||||
|
import java.util.HashMap
|
||||||
|
import kotlin.properties.delegation.*
|
||||||
|
|
||||||
|
class MapDelegationTest(): DelegationTestBase() {
|
||||||
|
|
||||||
|
fun testMapPropertyString() {
|
||||||
|
doTest(TestMapPropertyString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMapValWithDifferentTypes() {
|
||||||
|
doTest(TestMapValWithDifferentTypes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMapVarWithDifferentTypes() {
|
||||||
|
doTest(TestMapVarWithDifferentTypes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNullableKey() {
|
||||||
|
doTest(TestNullableKey())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMapPropertyKey() {
|
||||||
|
doTest(TestMapPropertyKey())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMapPropertyFunction() {
|
||||||
|
doTest(TestMapPropertyFunction())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMapPropertyCustom() {
|
||||||
|
doTest(TestMapPropertyCustom())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMapValWithDefault() {
|
||||||
|
doTest(TestMapValWithDefault())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMapVarWithDefault() {
|
||||||
|
doTest(TestMapVarWithDefault())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMapPropertyCustomWithDefault() {
|
||||||
|
doTest(TestMapPropertyCustomWithDefault())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestMapValWithDifferentTypes(): WithBox {
|
||||||
|
val map = hashMapOf("a" to "a", "b" to 1, "c" to A(1), "d" to null)
|
||||||
|
val a by map.readOnlyProperty<String>()
|
||||||
|
val b by map.readOnlyProperty<Int>()
|
||||||
|
val c by map.readOnlyProperty<Any>()
|
||||||
|
val d by map.readOnlyProperty<Int?>()
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
if (a != "a") return "fail at 'a'"
|
||||||
|
if (b != 1) return "fail at 'b'"
|
||||||
|
if (c != A(1)) return "fail at 'c'"
|
||||||
|
if (d != null) return "fail at 'd'"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
data class A(val a: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestMapVarWithDifferentTypes(): WithBox {
|
||||||
|
val map: HashMap<String, Any?> = hashMapOf("a" to "a", "b" to 1, "c" to A(1), "d" to "d")
|
||||||
|
var a by map.property<String>()
|
||||||
|
var b by map.property<Int>()
|
||||||
|
var c by map.property<Any>()
|
||||||
|
var d by map.property<String?>()
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
a = "aa"
|
||||||
|
b = 11
|
||||||
|
c = A(11)
|
||||||
|
d = null
|
||||||
|
if (a != "aa") return "fail at 'a'"
|
||||||
|
if (b != 11) return "fail at 'b'"
|
||||||
|
if (c != A(11)) return "fail at 'c'"
|
||||||
|
if (d != null) return "fail at 'd'"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
data class A(val a: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestNullableKey: WithBox {
|
||||||
|
val map = hashMapOf(null to "null")
|
||||||
|
var a by map.property<String?> { desc -> null }
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
if (a != "null") return "fail at 'a'"
|
||||||
|
a = "foo"
|
||||||
|
if (a != "foo") return "fail at 'a' after set"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestMapPropertyString(): WithBox {
|
||||||
|
val map = hashMapOf("a" to "a", "b" to "b", "c" to "c")
|
||||||
|
val a by map.readOnlyProperty<String>()
|
||||||
|
var b by map.property<String>()
|
||||||
|
val c by map.property<String>()
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
b = "newB"
|
||||||
|
if (a != "a") return "fail at 'a'"
|
||||||
|
if (b != "newB") return "fail at 'b'"
|
||||||
|
if (c != "c") return "fail at 'c'"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestMapValWithDefault(): WithBox {
|
||||||
|
val map = hashMapOf<String, String>()
|
||||||
|
val a by map.readOnlyProperty<String>(default = { "aDefault" })
|
||||||
|
val b by map.readOnlyProperty<String>(default = { "bDefault" }, key = "b")
|
||||||
|
val c by map.readOnlyProperty<String>(default = { "cDefault" }, key = { desc -> desc.name })
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
if (a != "aDefault") return "fail at 'a'"
|
||||||
|
if (b != "bDefault") return "fail at 'b'"
|
||||||
|
if (c != "cDefault") return "fail at 'c'"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestMapVarWithDefault(): WithBox {
|
||||||
|
val map = hashMapOf<String, String>()
|
||||||
|
var a by map.property<String>(default = { "aDefault" })
|
||||||
|
var b by map.property<String>(default = { "bDefault" }, key = "b")
|
||||||
|
var c by map.property<String>(default = { "cDefault" }, key = { desc -> desc.name })
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
if (a != "aDefault") return "fail at 'a'"
|
||||||
|
if (b != "bDefault") return "fail at 'b'"
|
||||||
|
if (c != "cDefault") return "fail at 'c'"
|
||||||
|
a = "a"
|
||||||
|
b = "b"
|
||||||
|
c = "c"
|
||||||
|
if (a != "a") return "fail at 'a' after set"
|
||||||
|
if (b != "b") return "fail at 'b' after set"
|
||||||
|
if (c != "c") return "fail at 'c' after set"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestMapPropertyKey(): WithBox {
|
||||||
|
val map = hashMapOf("a" to "a", "b" to "b")
|
||||||
|
val a by map.readOnlyProperty<String>(key = "a")
|
||||||
|
var b by map.property<String>(key = "b")
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
b = "c"
|
||||||
|
if (a != "a") return "fail at 'a'"
|
||||||
|
if (b != "c") return "fail at 'b'"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestMapPropertyFunction(): WithBox {
|
||||||
|
val map = hashMapOf("aDesc" to "a", "bDesc" to "b")
|
||||||
|
val a by map.readOnlyProperty<String> { desc -> "${desc.name}Desc" }
|
||||||
|
var b by map.property<String> { desc -> "${desc.name}Desc" }
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
b = "c"
|
||||||
|
if (a != "a") return "fail at 'a'"
|
||||||
|
if (b != "c") return "fail at 'b' after set"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val mapVal = object : MapVal<TestMapPropertyCustom, String>() {
|
||||||
|
override fun getMap(thisRef: TestMapPropertyCustom) = thisRef.map
|
||||||
|
override fun getKey(desc: PropertyMetadata) = "${desc.name}Desc"
|
||||||
|
}
|
||||||
|
val mapVar = object : MapVar<TestMapPropertyCustom, String>() {
|
||||||
|
override fun getMap(thisRef: TestMapPropertyCustom) = thisRef.map
|
||||||
|
override fun getKey(desc: PropertyMetadata) = "${desc.name}Desc"
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestMapPropertyCustom(): WithBox {
|
||||||
|
val map = hashMapOf("aDesc" to "a", "bDesc" to "b")
|
||||||
|
val a by mapVal
|
||||||
|
var b by mapVar
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
b = "newB"
|
||||||
|
if (a != "a") return "fail at 'a'"
|
||||||
|
if (b != "newB") return "fail at 'b' after set"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val mapValWithDefault = object : MapVal<TestMapPropertyCustomWithDefault, String>() {
|
||||||
|
override fun getMap(thisRef: TestMapPropertyCustomWithDefault) = thisRef.map
|
||||||
|
override fun getKey(desc: PropertyMetadata) = desc.name
|
||||||
|
|
||||||
|
override fun getDefaultValue(desc: PropertyMetadata, key: Any?) = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
val mapVarWithDefault = object : MapVar<TestMapPropertyCustomWithDefault, String>() {
|
||||||
|
override fun getMap(thisRef: TestMapPropertyCustomWithDefault) = thisRef.map
|
||||||
|
override fun getKey(desc: PropertyMetadata) = desc.name
|
||||||
|
|
||||||
|
override fun getDefaultValue(desc: PropertyMetadata, key: Any?) = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestMapPropertyCustomWithDefault(): WithBox {
|
||||||
|
val map = hashMapOf<String, String>()
|
||||||
|
val a by mapValWithDefault
|
||||||
|
var b by mapVarWithDefault
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
if (a != "default") return "fail at 'a'"
|
||||||
|
if (b != "default") return "fail at 'b'"
|
||||||
|
b = "c"
|
||||||
|
if (b != "c") return "fail at 'b' after set"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package test.properties.delegation.lazy
|
||||||
|
|
||||||
|
import kotlin.properties.delegation.lazy.*
|
||||||
|
import test.properties.delegation.WithBox
|
||||||
|
import test.properties.delegation.DelegationTestBase
|
||||||
|
|
||||||
|
class LazyValuesTest(): DelegationTestBase() {
|
||||||
|
|
||||||
|
fun testLazyVal() {
|
||||||
|
doTest(TestLazyVal())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNullableLazyVal() {
|
||||||
|
doTest(TestNullableLazyVal())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAtomicNullableLazyVal() {
|
||||||
|
doTest(TestAtomicNullableLazyVal())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAtomicLazyVal() {
|
||||||
|
doTest(TestAtomicLazyVal())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testVolatileNullableLazyVal() {
|
||||||
|
doTest(TestVolatileNullableLazyVal())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testVolatileLazyVal() {
|
||||||
|
doTest(TestVolatileLazyVal())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestLazyVal: WithBox {
|
||||||
|
var result = 0
|
||||||
|
val a by LazyVal {
|
||||||
|
++result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
a
|
||||||
|
if (a != 1) return "fail: initializer should be invoked only once"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestNullableLazyVal: WithBox {
|
||||||
|
var resultA = 0
|
||||||
|
var resultB = 0
|
||||||
|
|
||||||
|
val a: Int? by NullableLazyVal { resultA++; null}
|
||||||
|
val b by NullableLazyVal { foo() }
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
a
|
||||||
|
b
|
||||||
|
|
||||||
|
if (a != null) return "fail: a should be null"
|
||||||
|
if (b != null) return "fail: a should be null"
|
||||||
|
if (resultA != 1) return "fail: initializer for a should be invoked only once"
|
||||||
|
if (resultB != 1) return "fail: initializer for b should be invoked only once"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(): String? {
|
||||||
|
resultB++
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestAtomicLazyVal: WithBox {
|
||||||
|
var result = 0
|
||||||
|
val a by AtomicLazyVal {
|
||||||
|
++result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
a
|
||||||
|
if (a != 1) return "fail: initializer should be invoked only once"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestVolatileNullableLazyVal: WithBox {
|
||||||
|
var resultA = 0
|
||||||
|
var resultB = 0
|
||||||
|
|
||||||
|
val a: Int? by VolatileNullableLazyVal { resultA++; null}
|
||||||
|
val b by VolatileNullableLazyVal { foo() }
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
a
|
||||||
|
b
|
||||||
|
|
||||||
|
if (a != null) return "fail: a should be null"
|
||||||
|
if (b != null) return "fail: a should be null"
|
||||||
|
if (resultA != 1) return "fail: initializer for a should be invoked only once"
|
||||||
|
if (resultB != 1) return "fail: initializer for b should be invoked only once"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(): String? {
|
||||||
|
resultB++
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestVolatileLazyVal: WithBox {
|
||||||
|
var result = 0
|
||||||
|
val a by VolatileLazyVal {
|
||||||
|
++result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
a
|
||||||
|
if (a != 1) return "fail: initializer should be invoked only once"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestAtomicNullableLazyVal: WithBox {
|
||||||
|
var resultA = 0
|
||||||
|
var resultB = 0
|
||||||
|
|
||||||
|
val a: Int? by AtomicNullableLazyVal { resultA++; null}
|
||||||
|
val b by AtomicNullableLazyVal { foo() }
|
||||||
|
|
||||||
|
override fun box(): String {
|
||||||
|
a
|
||||||
|
b
|
||||||
|
|
||||||
|
if (a != null) return "fail: a should be null"
|
||||||
|
if (b != null) return "fail: a should be null"
|
||||||
|
if (resultA != 1) return "fail: initializer for a should be invoked only once"
|
||||||
|
if (resultB != 1) return "fail: initializer for b should be invoked only once"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(): String? {
|
||||||
|
resultB++
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user