Kotlin: Check if lazy val has been initialised - kotlin

Is there a way to tell if a lazy val has been initialised in Kotlin without initialising it in the process?
eg if I have a lazy val, querying if it is null would instantiate it
val messageBroker: MessageBroker by lazy { MessageBroker() }
if (messageBroker == null) {
// oops
}
I could potentially use a second variable, but that seems messy.
private var isMessageBrokerInstantiated: Boolean = false
val messageBroker: MessageBroker by lazy {
isMessageBrokerInstantiated = true
MessageBroker()
}
...
if (!isMessageBrokerInstantiated) {
// use case
}
Is there some sexy way of determining this, like if (Lazy(messageBroker).isInstantiated())?
Related (but not the same): How to check if a "lateinit" variable has been initialized?

There is a way, but you have to access the delegate object which is returned by lazy {}:
val messageBrokerDelegate = lazy { MessageBroker() }
val messageBroker by messageBrokerDelegate
if(messageBrokerDelegate.isInitialized())
...
isInitialized is a public method on interface Lazy<T>, here are the docs.

Since Kotlin 1.1, you can access a property delegate directly using .getDelegate().
You can write an extension property for a property reference that checks that it has a Lazy delegate that has already been initialized:
/**
* Returns true if a lazy property reference has been initialized, or if the property is not lazy.
*/
val KProperty0<*>.isLazyInitialized: Boolean
get() {
if (this !is Lazy<*>) return true
// Prevent IllegalAccessException from JVM access check on private properties.
val originalAccessLevel = isAccessible
isAccessible = true
val isLazyInitialized = (getDelegate() as Lazy<*>).isInitialized()
// Reset access level.
isAccessible = originalAccessLevel
return isLazyInitialized
}
Then at the use site:
val messageBroker: MessageBroker by lazy { MessageBroker() }
if (this::messageBroker.isLazyInitialized) {
// ... do stuff here
}
This solution requires kotlin-reflect to be on the classpath. With Gradle, use compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
The isAccessible = true part is required for the .getDelegate(), because otherwise it cannot access the private field storing the delegate reference.

Testing if the lazy property is easy enough:
import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible
val KProperty0<*>.isLazyInitialized: Boolean
get() {
// Prevent IllegalAccessException from JVM access check
isAccessible = true
return (getDelegate() as Lazy<*>).isInitialized()
}
…but you can make it even easier to reference a property without initializing it:
/**
* Returns the value of the given lazy property if initialized, null
* otherwise.
*/
val <T> KProperty0<T>.orNull: T?
get() = if (isLazyInitialized) get() else null
Now you can do things like:
private val myList by lazy {
mutableSetOf<String>()
}
fun add(str: String) {
// Create the list if necessary
myList += str
}
fun remove(str: String) {
// Don't create the list
::myList.orNull?.remove(str)
}
fun clear() {
// Don't create the list
::myList.orNull?.clear()
}

Related

Attempt to invoke interface method on a null object reference in kotlin

After implementing viewmodels to jetpack compose app when I running the app it's showing a error :-
Attempt to invoke interface method 'boolean java.util.Set.contains(java.lang.Object)' on a null object reference
java.lang.NullPointerException: Attempt to invoke interface method 'boolean java.util.Set.contains(java.lang.Object)' on a null object reference
at com.example.android.ui.GameViewModel.pickRandomWordAndShuffle(GameViewModel.kt:21)
at com.example.android.ui.GameViewModel.(GameViewModel.kt:10)
here is my code:-
import androidx.lifecycle.ViewModel
import com.example.android.unscramble.data.allWords
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class GameViewModel : ViewModel() {
private val _uiState =
MutableStateFlow(GameUiState(currentScrambledWord = pickRandomWordAndShuffle()))
val uiState: StateFlow<GameUiState> = _uiState.asStateFlow()
private var _count = 0
val count: Int
get() = _count
private lateinit var currentWord: String
private var usedWords: MutableSet<String> = mutableSetOf()
private fun shuffleCurrentWord(word: String): String {
val tempWord = word.toCharArray()
// Scramble the word
tempWord.shuffle()
while (String(tempWord) == word) {
tempWord.shuffle()
}
return String(tempWord)
}
private fun pickRandomWordAndShuffle(): String {
// Continue picking up a new random word until you get one that hasn't been used before
currentWord = allWords.random()
if (usedWords.contains(currentWord)) {
return pickRandomWordAndShuffle()
} else {
usedWords.add(currentWord)
return shuffleCurrentWord(currentWord)
}
}
private fun resetGame() {
usedWords.clear()
_uiState.value = GameUiState(currentScrambledWord = pickRandomWordAndShuffle())
}
init {
resetGame()
}
}
It's not showing any compile time errors. I don't konw what should I do.
You're initializing _uiState before you initialize usedWords. This calls pickRandomWordAndShuffle() before usedWords has been initialized, so it's still null in the GameViewModel instance being created.
If you move the declaration of usedWords above _uiState it should work.
HOWEVER: It's generally a bad idea to call member functions before an instance has been fully initialized, for this exact reason.
You could make _uiState and uiState lazy, which would make this safer. For example:
// Copyright 2023 Google LLC.
// SPDX-License-Identifier: Apache-2.0
private val _uiState by lazy {
MutableStateFlow(GameUiState(currentScrambledWord = pickRandomWordAndShuffle()))
}
val uiState: StateFlow<GameUiState> by lazy { _uiState.asStateFlow() }
which will wait until something uses uiState (which looking at your code only happens externally, so you'll be guaranteed that it won't be initialized until the GameViewModel has been fully initialized.

How to express in Kotlin "assign value exactly once on the first call"?

Looking for a natural Kotlin way to let startTime be initialized only in a particular place and exactly once.
The following naive implementation have two problems:
it is not thread safe
it does not express the fact "the variable was or will be assigned exactly once in the lifetime of an Item instance"
class Item {
var startTime: Instant?
fun start(){
if (startTime == null){
startTime = Instant.now()
}
// do stuff
}
}
I believe some kind of a delegate could be applicable here. In other words this code needs something similar to a lazy variable, but without initialization on first read, instead it happens only after explicit call of "touching" method. Maybe the Wrap calls could give an idea of possible implementation.
class Wrap<T>(
supp: () -> T
){
private var value: T? = null
private val lock = ReentrantLock()
fun get(){
return value
}
fun touch(){
lock.lock()
try{
if (value == null){
value = supp()
} else {
throw IllegalStateExecption("Duplicate init")
}
} finally{
lock.unlock()
}
}
}
How about combining AtomicReference.compareAndSet with a custom backing field?
You can use a private setter and make sure that the only place the class sets the value is from the start() method.
class Item(val value: Int) {
private val _startTime = AtomicReference(Instant.EPOCH)
var startTime: Instant?
get() = _startTime.get().takeIf { it != Instant.EPOCH }
private set(value) = check(_startTime.compareAndSet(Instant.EPOCH, value)) { "Duplicate set" }
fun start() {
startTime = Instant.now()
}
override fun toString() = "$value: $startTime"
}
fun main() = runBlocking {
val item1 = Item(1)
val item2 = Item(2)
println(Instant.now())
launch { println(item1); item1.start(); println(item1) }
launch { println(item1) }
delay(1000)
println(item2)
item2.start()
println(item2)
println(item2)
item2.start()
}
Example output:
2021-07-14T08:20:27.546821Z
1: null
1: 2021-07-14T08:20:27.607365Z
1: 2021-07-14T08:20:27.607365Z
2: null
2: 2021-07-14T08:20:28.584114Z
2: 2021-07-14T08:20:28.584114Z
Exception in thread "main" java.lang.IllegalStateException: Duplicate set
I think your Wrap class is a good starting point to implement this. I would definitely make it a property delegate and touch() could be much simplified:
fun touch() {
synchronized(this) {
check(value == null) { "Duplicate init" }
value = supp()
}
}
Then you can remove lock. But generally, this is a good approach.
If you would like to reuse lazy util from stdlib then you can do this by wrapping it with another object which does not read its value until asked:
class ManualLazy<T : Any>(private val lazy: Lazy<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? {
return if (lazy.isInitialized()) lazy.value else null
}
fun touch() {
lazy.value
}
}
class Item {
private val _startTime = ManualLazy(lazy { Instant.now() })
val startTime: Instant? by _startTime
fun start(){
_startTime.touch()
}
}
Of course, depending on your needs you can implement it in a much different way, using a similar technique.
This may be considered exploiting or hacking lazy util. I agree and I think Wrap approach is a better one.

How to find is a lateinit var has been initialized via Reflection?

I want to find out via reflection if lateinit property of an object has been initialized. How do I got about doing that?
Getting the property and checking non-null results in UninitializedPropertyAccessException
fun Any.isAnyEntityInitialized () {
val clazz = this.javaClass.kotlin
val filteredEntities = clazz.declaredMemberProperties.filter {
it.isLateinit && getDelegate(this) != null
}
}
This works for me:
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.jvm.javaField
class Test {
lateinit var s: String
}
fun Any.isAnyEntityInitialized(): Boolean =
this::class.declaredMemberProperties.any { property ->
property.isLateinit && property.javaField?.get(this) != null
}
fun main() {
val test = Test()
println(test.isAnyEntityInitialized()) // prints false
test.s = "test"
println(test.isAnyEntityInitialized()) // prints true
}
Since Kotlin 1.2, there is already a function for that.
You can use the function: isInitialized docs
Code example:
lateinit var key: String
fun useKey() {
check(::key.isInitialized) { "The field 'key' must be initialized" }
// use key safely
}
Note: check will throw an IllegalStateException if the variable is not initialized.

Mock private property with mockk throws an excpetion

I'm using mockk for my testing in kotlin. But I can't seem to override a private property in a spy object.
I have this object
private val driverMapSnapshotMap: MutableMap<Int, SnapshotImage> = mutableMapOf()
in a class that I spy on using
viewModel = spyk(DriverListViewModel(), recordPrivateCalls = true)
But when I try to make it fill up with mock values I get an error
every {
viewModel getProperty "driverMapSnapshotMap"
} returns(mapOf(1 to mockkClass(SnapshotImage::class)))
The error I get
io.mockk.MockKException: Missing calls inside every { ... } block.
Any thoughts?
Here is a solution to access private fields in Mockk for classes( for objects it is even simpler )
class SaySomething {
private val prefix by lazy { "Here is what I have to say: "}
fun say( phrase : String ) : String {
return prefix+phrase;
}
}
#Before
fun setUp() = MockKAnnotations.init(this, relaxUnitFun = true)
#Test
fun SaySomething_test() {
mockkConstructor(SaySomething::class)
every { anyConstructed<SaySomething>() getProperty "prefix" } propertyType String::class returns "I don't want to say anything, but still: "
val ss = SaySomething()
assertThat( ss.say("Life is short, make most of it"), containsString( "I don't want to say anything"))
}
It is nearly impossible to mock private properties as they don't have getter methods attached. This is kind of Kotlin optimization and solution is major change.
Here is issue opened for that with the same problem:
https://github.com/mockk/mockk/issues/263
It should be
every {
viewModel getProperty "driverMapSnapshotMap"
} returns mock(DriverRemoteModel::class)

How to access variables in kotlin without invoking its setter and getter

I am working on a library where any change in argument refreshes the view. In refresh() function, I am setting some arguments' values to default values.
var viewAlpha= 255
set(value) {
field = value
refresh()
}
fun refresh() {
viewAlpha = 255
invalidate()
}
This is causing StackOverflowError due to obvious reasons.
Caused by: java.lang.StackOverflowError: stack size 8MB
Is it possible to access variables in kotlin without invoking its setter when we are accessing it in the same class. Similar to what we do in java.
One way would be to provide you good ol' backing field to get out of setter-refres cycle:
private var _viewAlpha = 255
var viewAlpha
get() {
return _viewAlpha
}
set(value) {
_viewAlpha = value
refresh()
}
fun refresh() {
_viewAlpha = 255
invalidate()
}
If you want to simplify your logic for multiple fields you can abstract this implementation into separate class and use callback call with direct setter that will work without refresh invocation. Like this:
class Field(val onSetCb: (Field) -> Unit) {
private var viewAlpha = 255
fun get() {
return viewAlpha
}
fun set(value: Int) {
setDirect(value)
onSetCb(this)
}
fun setDirect(value: Int) {
viewAlpha = value;
}
}
// Elsewhere...
fun refresh(field: Field) {
field.setDirect(255)
invalidate()
}
val f = Field(::refresh)
f.set(255)