Skip to content

Instantly share code, notes, and snippets.

@rock3r
Created January 8, 2019 13:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rock3r/6fc350397863efd59060b024d963e15a to your computer and use it in GitHub Desktop.
Save rock3r/6fc350397863efd59060b024d963e15a to your computer and use it in GitHub Desktop.
A cacheable property in Kotlin — basically, a variant of Lazy that can be invalidated and will be reloaded on the next access
package me.seebrock3r.util
import me.seebrock3r.util.CacheableProperty.CachedValue.*
import kotlin.properties.*
import kotlin.reflect.*
import kotlin.reflect.jvm.*
fun <T> cache(producer: () -> T): CacheableProperty<T> = CacheableProperty(producer)
class CacheableProperty<out T>(val producer: () -> T) : ReadOnlyProperty<Any, T> {
private var value: CachedValue<T> = CachedValue.Nothing
override fun getValue(thisRef: Any, property: KProperty<*>): T =
when (value) {
is CachedValue.Nothing -> producer().also { value = CachedValue.Something(it) }
is CachedValue.Something<T> -> (value as Something<T>).value
}
fun invalidate() {
value = CachedValue.Nothing
}
private sealed class CachedValue<out T> {
class Something<out T>(val value: T) : CachedValue<T>()
object Nothing : CachedValue<kotlin.Nothing>()
}
}
fun KProperty0<*>.invalidateCacheableProperty() {
val wasAccessible = isAccessible
isAccessible = true
if (getDelegate() !is CacheableProperty<*>) {
throw UnsupportedOperationException("Only CacheableProperty supports invalidation")
}
(getDelegate() as CacheableProperty<*>).invalidate()
isAccessible = wasAccessible
}
internal class UsageExample(private val path: Path) {
// Silly example, you could not do all this but whatevs
private val _lines by lazy { Files.readAllLines(path).toMutableList() }
val lines: List<String>
get() = _lines.toList()
val linesCount
get() = _lines.size
val contents by cache { _lines.joinToString(separator = "\n") }
fun addLine(lineText: String, insertPosition: Int = linesCount) {
_lines.add(insertPosition, lineText)
invalidateCacheBecauseContentsChanged()
}
private fun invalidateCacheBecauseContentsChanged() {
::contents.invalidateCacheableProperty()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment