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).
Related
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.
In Kotlin when you create a getter/setter pair, you typically set the getter using inline code. But I am wondering if it is possible to replace the inline code with an anonymous function:
var UserSettings: UserSettings?
get() = getUserSettings() // Replace this with an anonymous function?
set(value) {
putPref(USER_SETTINGS, Json.stringify(UserSettings.serializer(), value!!))
}
private fun getUserSettings(): UserSettings? {
val info = getPref(KEY_USER_SETTINGS)
return Json.parse(UserSettings.serializer(), info!!)
}
Can the getUserSettings() be replaced with an anonymous function? In the code above I have a separate function getUserSettings that I would like to place right after the get() =
Yes, you can. Just have a look at getters and setters - backing properties, where there is (the first and only example), mentioning get() { instead of get() =. Your sample would then look as follows:
var UserSettings: UserSettings?
get() {
val info = getPref(KEY_USER_SETTINGS)
return Json.parse(UserSettings.serializer(), info!!)
}
set(value) {
putPref(USER_SETTINGS, Json.stringify(UserSettings.serializer(), value!!))
}
i don't know this is what you asking for but it might be helpful
var v: Int? = null
get() = run {
return field
}
set(value) = run {
field = value
}
in this case getter must be equel to Int? and setter allways must be equel to Unit. so in the run we return that types
I think the answer of #Roland is valid.
Be aware that maintaining the = before the anonymous function the compiler returns an error as you are describing.
Can you double check that you are NOT writing something like this?
var UserSettings: UserSettings?
get() = { ... }
And that you are writing:
var UserSettings: UserSettings?
get() { ... }
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)
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()
}
object WalletConfig {
init {
fill(HashMap<String, String>())
}
var clientUrl: String by Delegates.notNull()
private set
fun fill(settingMap: Map<String, String>) {
try {
clientUrl = settingMap["URL_CLIENT"] ?: ""
} catch(ex:Exception) { }
}
}
throw ex: fill: Load 'wallet.config' config file: java.lang.NullPointerException at ru.WalletConfig.setClientUrl(WalletConfig.kt)
The problem is caused by the miss ordered init block and property initializer. As stated in the answer about class initialization semantics. The init block is not the constructor - its code is included in the primary constructor.
The following example:
class OrderOfInits {
constructor() {
println("constructor")
}
init {
println("A")
}
init {
println("B")
}
init {
println("C")
}
}
val a = OrderOfInits()
Would print:
A
B
C
constructor
Property delegates initialization is also part of primary constructor. Their initialization order in constructor reflects the order they were declared in. In other words the init { fill(...) } initializer block invokes clientUrl setter which in turn tries to call Delegates.notNull() instance method. However the instance field holding the Delegates.notNull() was not yet set hence NPE.
To fix the problem you just need to reorder them like so:
object WalletConfig {
var clientUrl: String by Delegates.notNull()
private set
init {
fill(HashMap<String, String>())
}
...
}