How can you enforce a setter method to be called on creating a object that can produce compile time warning or exception? - kotlin

Suppose I've a class A which looked like this before:
class A(str:String){
// body
}
Now, I want to remove the parameter from the constructor instead I'll be using a setter for that value. Like below:
class A(){
lateinit var str:String
fun setStr(paramsString:String){
str = paramsString
}
}
As I am using a setter for assigning the value, it wont give me a compile time exception while creating the object of that class.
So, I tried something like below:
class A(){
lateinit var str:String
init{
setStr(strValue:String)
}
fun setStr(paramsString:String){
str = paramsString
}
}
This init block will be executed after creating the object of the class A where the setter method will be called.
But still what I want is to warn or throwing an exception when the object of the following class is created for calling the setter method.

This here
class A(public str:String){
// body
}
already has a setter, since yo can do
A("Bla"); println(a.str)
compile time errors would probably need some type checking (e.g. String? allows null, String checks for not null)
if you want to execute code on a.str="Blub" you do this:
class A(){
var str:String = ""
set(str) {
// check some stuff here, throw Exception
field = str // if all is well
}
}
you can also do similar for get() BTW.
Another method to do stuff on setting is using var a: String by Delegate.vetoable.... and other Delegate methods - giving you high control over what's happening on setting a new value.
class A {
var str: String = ""
set(str) {
// check some stuff here, throw Exception
field = str // if all is well
}
var observableDelegate: String by Delegates.observable("") { p, o, n ->
println("property: [$p], Old: [$o], New [$n]")
}
var notNullValue: String by Delegates.notNull<String>()
var vetoable: String by Delegates.vetoable("") { property, oldValue, newValue ->
println("property: $property, old: $oldValue, new: $newValue")
newValue != "bla" && oldValue < newValue
}
}
fun main() {
val a = A()
a.observableDelegate = "blub"
println("Set value ${a.observableDelegate}")
runCatching { a.notNullValue }.onFailure {
println("Like expected: value not set yet, exception: ${it.message}")
}
a.notNullValue = "fuddder"
println("Now notNullValue is set: ${a.notNullValue}")
println("Vetoable now: ${a.vetoable} - setting to \"a\"")
a.vetoable = "a"
println("Vetoable now: ${a.vetoable} - setting to \"b\"")
a.vetoable = "b"
println("Vetoable now: ${a.vetoable} - setting to \"a\" again - should not work")
a.vetoable = "a"
println("Vetoable now: ${a.vetoable}")
}
output:
property: [var com.savinggrains.mwh.web.rest.A.observableDelegate: kotlin.String], Old: [], New [blub]
Set value blub
Like expected: value not set yet, exception: Property notNullValue should be initialized before get.
Now notNullValue is set: fuddder
Vetoable now: - setting to "a"
property: var com.savinggrains.mwh.web.rest.A.vetoable: kotlin.String, old: , new: a
Vetoable now: a - setting to "b"
property: var com.savinggrains.mwh.web.rest.A.vetoable: kotlin.String, old: a, new: b
Vetoable now: b - setting to "a" again - should not work
property: var com.savinggrains.mwh.web.rest.A.vetoable: kotlin.String, old: b, new: a
Vetoable now: b

Related

How to Observe LiveData with custom pair class in Kotlin

I am trying to observe the LiveData for the method which returns custom pair having 2 values. I want the observable to be triggered when I change either one of the values. But it is not getting triggered. Following is the code:
CustomPair.kt
data class CustomPair<T, V>(
var first : T,
var second : V
)
Observable:
falconViewModel.getPlanet1Name().observe(this) {
planet1.text = it.first
planet1.isEnabled = it.second
}
Getter and setter methods in ViewModel falconViewModel
private val planet1EnabledAndText = MutableLiveData<CustomPair<String, Boolean>>()
fun getPlanet1Name() : LiveData<CustomPair<String, Boolean>> {
return planet1EnabledAndText
}
fun setPlanet1Name(planetName : String, visibility : Boolean) {
planet1EnabledAndText.value?.run {
first = planetName
second = visibility
}
}
Can't we observe the value in such case? Please help what is wrong here.
It started working when I tried to set a new value of CustomPair instead of modifying the existing values in the object.
Replaced
planet1EnabledAndText.value = CustomPair(planetName, visibility)
with
planet1EnabledAndText.value?.run {
first = planetName
second = visibility
}
and it worked.

My non-nullable ArrayList is returning a null upon calling it and throwing a Null Pointer Exception

Running this on IntelliJ IDEA 2020.11 using JDK 14 and coding it in Kotlin.
I have a class with a variable menuComponents which is an ArrayList full of MenuComponents, but it's empty at initialization.
var menuComponents: ArrayList<MenuComponent> = ArrayList()
I want to edit the components so I wrote this.
for (component in menuComponents) {
//Do some stuff. The stuff doesn't matter, it throws an exception if I leave it blank
}
When I call on this method, I get a null pointer exception. Additionally, the for loop doesn't even matter.
class NPE() {
init {
refreshProperties()
}
var menuComponents: ArrayList<Double> = ArrayList()
fun refreshProperties() {
refreshJMenuComponents()
}
private fun refreshJMenuComponents() {
val i = menuComponents.size
println("$i is the length.")
for (index in 0 until menuComponents.size) {
val component = menuComponents[index]
println("Refreshed component: $component")
}
}
}
fun main() {
NPE()
}
This statement errors out too. I don't change menuComponents at all before I call these, so it should just be equal to a blank array list. I don't know why it's throwing a Null Pointer Exception.
menuComponents = arrayListOf(//component 1, component 2)
If I try running any of the previous statements on menuComponents now, it still throws a Null Pointer Exception. The value is not nullable, and I am explicitly setting it equal to something, so why is it erroring out at all? It should just not even compile if there is a null object somewhere? It compiles and then throws an exception.
Is this some sort of possible bug or am I just missing something?
I just needed to move the variable initialization above the init block.
class NPE() {
var menuComponents: ArrayList<Double> = ArrayList()
init {
refreshProperties()
}
fun refreshProperties() {
refreshJMenuComponents()
}
private fun refreshJMenuComponents() {
val i = menuComponents.size
println("$i is the length.")
for (index in 0 until menuComponents.size) {
val component = menuComponents[index]
println("Refreshed component: $component")
}
}
}
fun main() {
NPE()
}

Kotlin DSL variable imitation

Using Kotlin type-safe builders one might end up writing this code
code {
dict["a"] = "foo"; // dict is a Map hidden inside that can associate some name to some value
println(dict["a"]); // usage of this value
}
This code is ok, but there is a problem: "a" is just a string. I want it to be like a user-defined variable - an identifier that is recognized by the compiler, auto-complete enabled.
Is there a way to turn it into something like this?
code {
a = "foo"; // now 'a' is not a Map key, but an identifier recognized by Kotlin as a variable name
println(a);
}
I can do this if I make code's lambda an extension function over some object with a field a defined inside. This is not what I want. I want to be able to use other variables (with unknown names) as well.
A possible workaround could be
code {
var a = v("a", "foo");
println(a);
}
Where v is a method of the extension's object, that stores value "foo" inside "dict" and also returns a handle to this value.
This case is almost perfect, but can it be clearer/better somehow?
You can use property delegation:
class Code {
private val dict = mutableMapOf<String, String>()
operator fun String.provideDelegate(
thisRef: Any?,
prop: KProperty<*>
): MutableMap<String, String> {
dict[prop.name] = this
return dict
}
}
Use case:
code {
var a by "foo" // dict = {a=foo}
println(a) // foo
a = "bar" // dict = {a=bar}
println(a) // bar
}

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.

"Cannot assign to the result of this expression" - How to set property of optional variable

I have a question regarding Swift and optional properties.
Suppose I have the following code:
import Foundation
class MyClass: NSObject {
var parent: MyClass?
var string: String?
init() {
super.init()
}
}
let variable : MyClass = MyClass()
variable.string = "variable"
variable.parent?.string = "parent"
I get an error on the following line saying "Cannot assign to the result of this expression"
variable.parent?.string = "parent"
Now, I can suppress this error by replacing the question mark with an exclamation mark which, to my knowledge, makes Swift assume that an object will be there at runtime but it crashes because obviously there isn't an object.
Essentially, how can I set a property on an optional variable without doing something like "if variable.parent"?
To work with optional, you need to use optional chainning
let variable : MyClass = MyClass()
variable.string = "variable"
variable.parent = MyClass()
if let a = variable.parent {
a.string = "parent"
}
or if you are sure there is object there you can do
variable.parent!.string = "parent"
note that if you did not initialize parent you will get an error.
You've can't be assigning the string in a non-existent parent. It will be up to you how to avoid that. One thing for sure, either you will ensure the parent is bound (like getting it bound in some init() method) or you must use if variable.parent each time (even though you don't want to) or you must define a method like assignParentString() that is implemented to check on the parent.
func assignParentString (string: String) {
if let parent = self.parent {
parent.string = string
}
}
For example:
3> class MyClass {
4. var parent : MyClass?
5. var name : String?
6. func aps (name:String) {
7. if let parent = self.parent {
8. parent.name = name
9. }
10. }
11. }
12> var mc = MyClass()
mc: MyClass = {
parent = nil
name = nil
}
13> mc.name = "mc"
14> mc.aps ("mcp") // no error, name did not take
15> mc
$R3: MyClass = {
parent = nil
name = "mc"
}
16> mc.parent = MyClass()
17> mc.aps ("mcp")
18> mc
$R6: MyClass = {
parent = Some {
parent = nil
name = "mcp"
}
name = "mc"
}