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.
Related
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.
I am new to kotlin. I wonder if this is possible
I wish to create a function that will change the value of the properties of the object and return the object itself. The main benefit is that I can chain this setter.
class Person {
var name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
try {
// the line below caused error
this[propName] = value
} catch(e:Exception){
println(e.printStackTrace())
}
}
}
}
//usage
var person = Person(null,null)
person
.setter(name, "Baby")
.setter(age, 20)
But I get error "unknown references"
This question is marked as duplicate, however the possible duplicate question specifically want to change the property of "name", but I wish to change anyProperty that is pass from the function to object. Can't seem to connect the dot between two questions. #Moira Kindly provide answer that explain it. thankyou
Why not just simplify your answer to
fun setter(propName: String, value: Any): Person {
val property = this::class.memberProperties.find { it.name == propName }
when (property) {
is KMutableProperty<*> ->
property.setter.call(this, value)
null ->
// no such property
else ->
// immutable property
}
}
Java reflection isn't needed, its only effect is to stop non-trivial properties from being supported.
Also, if you call it operator fun set instead of fun setter, the
this[propName] = value
syntax can be used to call it.
After googling around, I think I can provide an answer, but relying on java instead of kotlin purely. It will be great if someone can provide a better answer in kotlin.
class Person(
var name: String,
val age: Int
){
fun setter(propName: String, value: Any): Person{
var isFieldExistAndNotFinal = false
try{
val field = this.javaClass.getDeclaredField(propName)
val isFieldFinal = (field.getModifiers() and java.lang.reflect.Modifier.FINAL == java.lang.reflect.Modifier.FINAL)
if(!isFieldFinal) {
// not final
isFieldExistAndNotFinal = true
}
// final variable cannot be changed
else throw ( Exception("field '$propName' is constant, in ${this.toString()}"))
} catch (e: Exception) {
// object does not have property
println("$e in ${this.toString()}")
}
if(isFieldExistAndNotFinal){
val property = this::class.memberProperties.find { it.name == propName }
if (property is KMutableProperty<*>) {
property.setter.call(this, value)
}
}
return this;
}
}
usage like this
person
.setter(propName = "age", value = 30.00)
.setter(propName = "asdf", value = "asdf")
.setter(propName = "name", value = "A Vidy")
You have error because when you do this[propName] = value you are trying to use this as a list, but it is not a list, it is a Person and it doesn't overload the [] operator.
What you can do is to add a check for the property that is setted:
class Person {
privavar name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
if (propName == "name" && value is String?) {
it.name = value as String?
} else if (propName == "age" && value is Int?) {
it.age = value as Int?
} else {
// handle unknown property or value has incorrect type
}
}
}
}
Another more dynamic solution without reflection:
class Person {
private var fields: Map<String, Any?> = HashMap()
fun setter(propName:String, value:Any): Person{
return this.apply {
it.fields[propName] = value;
}
}
fun getName() = fields["name"]
}
If you want to get rid of the getters as well then you need to use reflection.
The following code does not compile:
describe("something") {
context("when something") {
var a: SomeType
beforeEachTest {
a = someNewMutableObject
}
it("should do something") {
assertTrue(a.something()) // variable a not initialized
}
}
}
How would one get around this problem? What could i assign to the variable to get rid of the warning?
Just use the lateinit modifier on the variable that will be initialised before use.
describe("something") {
context("when something") {
lateinit var a: SomeType
beforeEachTest {
a = someNewMutableObject
}
it("should do something") {
assertTrue(a.something()) // variable a is okay to use here
}
}
}
PS. lateinit local variables are available from Kotlin 1.2 only
In Kotlin 1.1 you should just initialise it to a default value or null (make it a nullable type also).
I'm wondering if it's possible in Kotlin to deserialize (restore property values) of a declared object, without having to manually assign the properties or resorting to reflection. The following snippet further explains:
object Foo: Serializable {
var propOne: String = ""
// ...
fun persist() {
serialize(this)
// no problem with serialization
}
fun restore(bytes: ByteArray) {
val fooObj: Foo = deserialize(bytes) as Foo
// It seems Kotlin allows us to use singleton as type!
// obvioulsly either of the following is wrong:
// this = fooObj
// Foo = fooObj
// ... is there a way to 'recover' the singleton (object) other than
// manual assignment of properties (or reflection) ???
}
}
There is no way to reassign the global reference to a singleton with a new instance. At most you can write out the properties during serialization, and then on deserialization directly read the properties and mutate the state in the original object. It will require custom code for you to assign the properties into the object either by direct assignment or reflection. It would be better if you make your own singleton mechanism that holds an instance that you can swap out to be another instance that you deserialize.
have faced with the same issue, and want to share with you my solution:
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import java.io.File
import java.lang.reflect.Modifier
typealias ObjMap = HashMap<String, Any?>
fun <T : Any> T.getInstance() : Any? {
val target = if(this is Class<*>) this else javaClass
return target.getDeclaredField("INSTANCE")?.get(null)
}
class ObjectHelper {
companion object {
val mapper = ObjectMapper().apply {
enable(SerializationFeature.INDENT_OUTPUT)
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}
fun objectToMap(obj: Any): ObjMap {
var res = ObjMap()
val instance = obj.getInstance()
val o = instance ?: obj
o.javaClass.declaredFields.forEach {
if(it.name != "INSTANCE") {
it.isAccessible = true
val value = if(Modifier.isStatic(it.modifiers)) it.get(null) else it.get(o)
res[it.name] = value
}
}
o.javaClass.classes.forEach {
res[it.simpleName] = objectToMap(it)
}
return res
}
fun saveObject(path: String, obj: Any) {
mapper.writeValue(File(path), objectToMap(obj))
}
fun loadObject(path: String, obj: Any) {
val json = mapper.readValue<HashMap<*,*>>(File(path), HashMap::class.java) as ObjMap
loadObject(obj, json)
}
fun loadObject(obj: Any, props: ObjMap) {
val objectParam = mapper.writeValueAsString(props)
mapper.readValue(objectParam, obj::class.java)
obj.javaClass.classes.forEach {
val instance = it.getInstance()
val map = props[it.simpleName]
if(map != null && instance != null) {
loadObject(instance, map as ObjMap)
}
}
}
}
}
Usage example:
object TestObj {
var f1: String = "f1"
var f2: String = "f2"
object TestObj_2 {
var f1: String = "f1_1"
var f2: String = "f2_2"
}
}
TestObj.f1 = "aaa"
saveObject("out.json", TestObj)
TestObj.f1 = "bbb"
loadObject("out.json", TestObj)
println(TestObj.f1)
Result will be "aaa".
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()
}