Provide Lazy<T> in kotlin package — an interface that represents lazily computed value. Read-only properties can be delegated to lazy with the extension getter. Delegates.lazy and blockingLazy are deprecated.
This commit is contained in:
@@ -49,4 +49,8 @@ public fun setOf<T>(value: T): Set<T> = hashSetOf(value)
|
||||
* Returns an immutable map, mapping only the specified key to the
|
||||
* specified value.
|
||||
*/
|
||||
public fun mapOf<K, V>(keyValuePair: Pair<K, V>): Map<K, V> = hashMapOf(keyValuePair)
|
||||
public fun mapOf<K, V>(keyValuePair: Pair<K, V>): Map<K, V> = hashMapOf(keyValuePair)
|
||||
|
||||
|
||||
public fun lazy<T>(initializer: () -> T): Lazy<T> = UnsafeLazyImpl(initializer)
|
||||
public fun lazy<T>(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> = UnsafeLazyImpl(initializer)
|
||||
|
||||
@@ -17,6 +17,7 @@ public object Delegates {
|
||||
* specified block of code. Supports lazy initialization semantics for properties.
|
||||
* @param initializer the function that returns the value of the property.
|
||||
*/
|
||||
deprecated("Use lazy {} instead in kotlin package", ReplaceWith("kotlin.lazy(initializer)"))
|
||||
public fun lazy<T>(initializer: () -> T): ReadOnlyProperty<Any?, T> = LazyVal(initializer)
|
||||
|
||||
/**
|
||||
@@ -27,6 +28,7 @@ public object Delegates {
|
||||
* the property delegate object itself is used as a lock.
|
||||
* @param initializer the function that returns the value of the property.
|
||||
*/
|
||||
deprecated("Use lazy {} instead in kotlin package", ReplaceWith("kotlin.lazy(initializer)"))
|
||||
public fun blockingLazy<T>(lock: Any? = null, initializer: () -> T): ReadOnlyProperty<Any?, T> = BlockingLazyVal(lock, initializer)
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package kotlin
|
||||
|
||||
|
||||
|
||||
public interface Lazy<out T> {
|
||||
public val value: T
|
||||
public val valueCreated: Boolean
|
||||
}
|
||||
|
||||
public fun <T> Lazy<T>.get(thisRef: Any?, property: PropertyMetadata): T = value
|
||||
|
||||
|
||||
public enum class LazyThreadSafetyMode {
|
||||
SYNCHRONIZED,
|
||||
NONE,
|
||||
}
|
||||
|
||||
|
||||
private object NULL_VALUE {}
|
||||
|
||||
private fun escape(value: Any?): Any {
|
||||
return value ?: NULL_VALUE
|
||||
}
|
||||
|
||||
private fun unescape(value: Any?): Any? {
|
||||
return if (value === NULL_VALUE) null else value
|
||||
}
|
||||
|
||||
|
||||
private class LazyImpl<out T>(initializer: () -> T, private val lockObj: Any) : Lazy<T> {
|
||||
private var initializer: (() -> T)? = initializer
|
||||
private volatile var _value: Any? = null
|
||||
|
||||
public override val value: T
|
||||
get() {
|
||||
val _v1 = _value
|
||||
if (_v1 != null) {
|
||||
return unescape(_v1) as T
|
||||
}
|
||||
|
||||
return synchronized(lockObj) {
|
||||
val _v2 = _value
|
||||
if (_v2 != null) {
|
||||
unescape(_v2) as T
|
||||
}
|
||||
else {
|
||||
val typedValue = initializer!!()
|
||||
_value = escape(typedValue)
|
||||
initializer = null
|
||||
typedValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val valueCreated: Boolean = _value == null
|
||||
|
||||
override fun toString(): String = if (valueCreated) value.toString() else "Lazy value not created yet."
|
||||
}
|
||||
|
||||
|
||||
private class UnsafeLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
|
||||
private var initializer: (() -> T)? = initializer
|
||||
|
||||
|
||||
private var _value: Any? = null
|
||||
|
||||
override val value: T
|
||||
get() {
|
||||
if (_value == null) {
|
||||
_value = escape(initializer!!())
|
||||
initializer == null
|
||||
}
|
||||
return unescape(_value) as T
|
||||
}
|
||||
|
||||
override val valueCreated: Boolean = _value == null
|
||||
|
||||
override fun toString(): String = if (valueCreated) value.toString() else "Lazy value not created yet."
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package kotlin
|
||||
|
||||
|
||||
public fun lazy<T>(initializer: () -> T): Lazy<T> = LazyImpl(initializer, Any())
|
||||
public fun lazy<T>(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
|
||||
when (mode) {
|
||||
LazyThreadSafetyMode.SYNCHRONIZED -> LazyImpl(initializer, Any())
|
||||
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
|
||||
}
|
||||
@@ -11,24 +11,34 @@ class LazyValuesTest(): DelegationTestBase() {
|
||||
doTest(TestLazyVal())
|
||||
}
|
||||
|
||||
test fun testUnsafeLazyVal() {
|
||||
doTest(TestUnsafeLazyVal())
|
||||
}
|
||||
|
||||
test fun testNullableLazyVal() {
|
||||
doTest(TestNullableLazyVal())
|
||||
}
|
||||
|
||||
test fun testAtomicNullableLazyVal() {
|
||||
doTest(TestAtomicNullableLazyVal())
|
||||
test fun testUnsafeNullableLazyVal() {
|
||||
doTest(TestUnsafeNullableLazyVal())
|
||||
}
|
||||
|
||||
test fun testAtomicLazyVal() {
|
||||
doTest(TestAtomicLazyVal())
|
||||
// deprecated lazy tests
|
||||
|
||||
test fun testUnsafeLazyValDeprecated() {
|
||||
doTest(TestUnsafeLazyValDeprecated())
|
||||
}
|
||||
|
||||
test fun testVolatileNullableLazyVal() {
|
||||
doTest(TestVolatileNullableLazyVal())
|
||||
test fun testBlockingLazyValDeprecated() {
|
||||
doTest(TestBlockingLazyValDeprecated())
|
||||
}
|
||||
|
||||
test fun testVolatileLazyVal() {
|
||||
doTest(TestVolatileLazyVal())
|
||||
test fun testUnsafeNullableLazyValDeprecated() {
|
||||
doTest(TestUnsafeNullableLazyValDeprecated())
|
||||
}
|
||||
|
||||
test fun testBlockingNullableLazyValDeprecated() {
|
||||
doTest(TestBlockingNullableLazyValDeprecated())
|
||||
}
|
||||
|
||||
test fun testIdentityEqualsIsUsedToUnescapeLazyVal() {
|
||||
@@ -37,6 +47,80 @@ class LazyValuesTest(): DelegationTestBase() {
|
||||
}
|
||||
|
||||
class TestLazyVal: WithBox {
|
||||
var result = 0
|
||||
val a by lazy {
|
||||
++result
|
||||
}
|
||||
|
||||
override fun box(): String {
|
||||
a
|
||||
if (a != 1) return "fail: initializer should be invoked only once"
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
class TestUnsafeLazyVal: WithBox {
|
||||
var result = 0
|
||||
val a by lazy(LazyThreadSafetyMode.NONE) {
|
||||
++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 lazy { resultA++; null}
|
||||
val b by lazy { 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 TestUnsafeNullableLazyVal: WithBox {
|
||||
var resultA = 0
|
||||
var resultB = 0
|
||||
|
||||
val a: Int? by lazy(LazyThreadSafetyMode.NONE) { resultA++; null}
|
||||
val b by lazy(LazyThreadSafetyMode.NONE) { 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 TestUnsafeLazyValDeprecated: WithBox {
|
||||
var result = 0
|
||||
val a by Delegates.lazy {
|
||||
++result
|
||||
@@ -49,7 +133,20 @@ class TestLazyVal: WithBox {
|
||||
}
|
||||
}
|
||||
|
||||
class TestNullableLazyVal: WithBox {
|
||||
class TestBlockingLazyValDeprecated: WithBox {
|
||||
var result = 0
|
||||
val a by Delegates.blockingLazy {
|
||||
++result
|
||||
}
|
||||
|
||||
override fun box(): String {
|
||||
a
|
||||
if (a != 1) return "fail: initializer should be invoked only once"
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
class TestUnsafeNullableLazyValDeprecated : WithBox {
|
||||
var resultA = 0
|
||||
var resultB = 0
|
||||
|
||||
@@ -73,57 +170,7 @@ class TestNullableLazyVal: WithBox {
|
||||
}
|
||||
}
|
||||
|
||||
class TestAtomicLazyVal: WithBox {
|
||||
var result = 0
|
||||
val a by Delegates.blockingLazy {
|
||||
++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 Delegates.blockingLazy { resultA++; null}
|
||||
val b by Delegates.blockingLazy { 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 Delegates.blockingLazy {
|
||||
++result
|
||||
}
|
||||
|
||||
override fun box(): String {
|
||||
a
|
||||
if (a != 1) return "fail: initializer should be invoked only once"
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
class TestAtomicNullableLazyVal: WithBox {
|
||||
class TestBlockingNullableLazyValDeprecated: WithBox {
|
||||
var resultA = 0
|
||||
var resultB = 0
|
||||
|
||||
@@ -149,7 +196,7 @@ class TestAtomicNullableLazyVal: WithBox {
|
||||
|
||||
class TestIdentityEqualsIsUsedToUnescapeLazyVal: WithBox {
|
||||
var equalsCalled = 0
|
||||
val a by Delegates.lazy { ClassWithCustomEquality { equalsCalled++ } }
|
||||
val a by lazy { ClassWithCustomEquality { equalsCalled++ } }
|
||||
|
||||
override fun box(): String {
|
||||
a
|
||||
|
||||
Reference in New Issue
Block a user