Created
August 23, 2021 16:41
-
-
Save belyaev-mikhail/ad7ca72ffd8ed7dcee24cbb648ed2887 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.spbstu.wheels | |
import kotlinx.warnings.Warnings | |
import kotlin.contracts.ExperimentalContracts | |
import kotlin.contracts.contract | |
import kotlin.properties.ReadOnlyProperty | |
import kotlin.properties.ReadWriteProperty | |
import kotlin.reflect.KProperty | |
abstract class GetterAndSetterBuilderEmpty<T> { | |
@PublishedApi | |
internal var getter: (() -> T)? = null | |
@PublishedApi | |
internal var setter: ((T) -> Unit)? = null | |
object DoneMarker | |
} | |
@OptIn(ExperimentalContracts::class) | |
fun <T> GetterAndSetterBuilderEmpty<T>.set(body: (T) -> Unit) { | |
contract { returns() implies (this@set is SetCalled) } | |
setter = body | |
} | |
@OptIn(ExperimentalContracts::class) | |
fun <T> GetterAndSetterBuilderEmpty<T>.get(body: () -> T) { | |
contract { returns() implies (this@get is GetCalled) } | |
getter = body | |
} | |
@Deprecated("You must call both get{} and set{} before signaling that you're done", | |
level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("get {}\nset {}")) | |
inline val <T> GetterAndSetterBuilderEmpty<T>.done: GetterAndSetterBuilderEmpty.DoneMarker | |
get() = GetterAndSetterBuilderEmpty.DoneMarker | |
inline val <GS, T> GS.done: GetterAndSetterBuilderEmpty.DoneMarker where GS: GetterAndSetterBuilderEmpty<T>, GS: GetCalled, GS: SetCalled | |
get() = GetterAndSetterBuilderEmpty.DoneMarker | |
interface GetCalled { | |
@Deprecated("Should only call get{} once", level = DeprecationLevel.ERROR) | |
fun <T> get(body: () -> T) {} | |
} | |
interface SetCalled { | |
@Deprecated("Should only call set{} once", level = DeprecationLevel.ERROR) | |
fun <T> set(body: (T) -> Unit) {} | |
} | |
class GetterAndSetterBuilder<T>: GetterAndSetterBuilderEmpty<T>(), GetCalled, SetCalled | |
object Delegates { | |
inline fun <T> getAndSet(crossinline body: GetterAndSetterBuilderEmpty<T>.() -> GetterAndSetterBuilderEmpty.DoneMarker) = object : ReadWriteProperty<Any?, T> { | |
val builder = GetterAndSetterBuilder<T>().apply{ body() } | |
init { | |
check(builder.getter != null) { "You need to call get {}" } | |
check(builder.setter != null) { "You need to call set {}" } | |
} | |
val getter = builder.getter!! | |
val setter = builder.setter!! | |
override fun getValue(thisRef: Any?, property: KProperty<*>): T = getter() | |
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = setter(value) | |
} | |
inline fun <T> get(crossinline body: () -> T) = ReadOnlyProperty<Any?, T> { _, _ -> body() } | |
} | |
sealed class OneShot<out T> { | |
internal abstract val value: T | |
class Value<out T>(override val value: T): OneShot<T>() | |
object Dead: OneShot<Nothing>() { | |
@Deprecated("Should only call get() once", level = DeprecationLevel.ERROR, | |
replaceWith = ReplaceWith("") | |
) | |
fun get(): Nothing = value | |
override val value: Nothing | |
get() = error("") | |
} | |
} | |
@OptIn(ExperimentalContracts::class) | |
fun <T> OneShot<T>.get(): T { | |
contract { returns() implies (this@get is OneShot.Dead) } | |
return value | |
} | |
fun <T> OneShot(value: T): OneShot<T> = OneShot.Value(value) | |
interface SimpleDelegate<T> { | |
fun get(): T | |
fun set(value: T) | |
} | |
inline operator fun <T, D: SimpleDelegate<T>> D.getValue(thisRef: Any?, property: KProperty<*>): T = | |
get() | |
inline operator fun <T, D: SimpleDelegate<T>> D.setValue(thisRef: Any?, property: KProperty<*>, value: T) = | |
set(value) | |
class LateInit<T>: SimpleDelegate<T> { | |
companion object { | |
private val UNINIT = Any() | |
} | |
private var actualVar: Any? = UNINIT | |
override fun get(): T = | |
@Suppress(Warnings.UNCHECKED_CAST) | |
if (actualVar == UNINIT) throw IllegalStateException("Lateinit property is not initialized") | |
else actualVar as T | |
override fun set(value: T) { | |
if (actualVar != UNINIT) throw IllegalStateException("Lateinit property assigned twice") | |
actualVar = value | |
} | |
} | |
fun main() { | |
var x: String by Delegates.getAndSet { | |
set { } | |
get { "" } | |
//get { "" } | |
done | |
} | |
val ff = OneShot(2) | |
ff.get() | |
//ff.get() | |
var xx: Int by LateInit() | |
xx = 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment