How to extend JavaFx Combobox class with new property in kotlin - kotlin

In my project with kotlin and tornadofx, I want to extend the JavaFx Combobox class, adding new property which will be used for storing specialized row items.
My code...
1. data class MsItemsClass(val id: String, val description: String)
2. var javafx.scene.control.ComboBox<String>.extItems: ObservableList<MsItemsClass> = emptyList<MsItemsClass>()
get() = this.extItems
at line 2. compiler put the error "Kotlin: Initializer is not allowed here because this property has no backing field"
Well, but I cannot know how resolve the problem.
Can enyone help me?

Related

Property would not be serialized into a Parcel warning after removing KAE

I'm removing KAE plugin to replace with kotlin-parcelize and i expect there is no change in behavior at all. But after that, i got warning on one of parcelable class. Here is my class.
#Parcelize
class SomeParcelableClass(
var foo: String
) : Parcelable {
#Transient
var bar: String? = null // warning here
}
The bar properties get Property would not be serialized into a Parcel warning. I look into documentation and found it:
#Parcelize requires all serialized properties to be declared in the
primary constructor. The plugin issues a warning on each property with
a backing field declared in the class body.
My question is, will the behavior change since there is no warning when using KAE plugin? Or this is still the same behavior and the warning just newly added to make it clear?
Is it safe to replace KAE with kotlin-parcelize in this condition?

How can I initialize a companion object property with a value taken from application.properties?

Is it possible to initialize a property of a companion object with some value read from application.properties?
I tried
// #Value("\${my.property.value}") ...doesn't work
val myProp: Duration by lazy { Duration.ofMinutes(#Value("\${my.property.value}")) }
but IntelliJ complains: Expecting an element.
Okay Duration.ofMinutes() requires a Long not a String but how can I achieve this using a configurable value in application.properties?
I solved it by using the #Value annotation in a service class now.
My intention was to have this value read in into a single place near the entity.

Use apply on a data class in Kotlin

I know how to use the apply function on a normal Kotlin class but have not been able to use it with a data class:
data class Person(name: String)
val person = Person().apply {
name = "Tony Stark"
}
I get a compile message of:
No value passed for parameter 'name'
The issue is that name is a constructor parameter only and not made a property, which is invalid for the data class concept anyway. Fix like this:
data class Person(val name: String)
The apply function works similar with any class. But there are some errors in your code snippet:
Parameter in Person constructor didn't mentioned as var or val, so there is no fields name in that class. It would be better to make it var to be able to change value.
You made class's constructor with 1 parameter, but trying to use empty constructor - it is error.

Handling nested Properties in JavaFX / TornadoFX

I want to have a window which shows info about certain ViewModel
Suppose you have a simple Person:
class Person(name: String) {
val nameProperty = SimpleStringProperty(name)
}
and have instance of Person save in property:
val personProperty = SimpleObjectProperty(Person("John"))
what's the correct solution to show Person's name in label?
Using this:
label(personProperty.value.nameProperty)
Will not update when I update the property's person:
personProperty.value = Person("Joe")
(That's obvious because only the reference changes, not the value itself)
So is there any good way to do this or do I have to manually add listeners for personProperty and update which Person does label point to?
EDIT:
I also found this question: JavaFX binding and property change, but it doesn't contain anything new and useful that I didn't know about, is there any TornadoFX-specific way of doing this?
This is exactly what the ItemViewModel does for you. If you want to make a binding for the name property that updates automatically, outside of an ItemViewModel, you can use the TornadoFX feature select:
val nameProperty = personProperty.select { it.nameProperty }
A listener can be attached to the property:
personProperty.onChange {
it?.nameProperty.let(nameLabel.textProperty().bind)
}
This can be wrapped in extension function to simplify the task.

Why Property must be initialized when there is auto back-end field generated

I'm new to properties and moved from the java to kotlin. I'm struggling with the properties, I learned much about it but initializing the properties are confusing me, when it should be initialized or when it can work without initialization.
Let me explain it by the help of code. Below is the code which is requiring to initialize the property when the back-end field generated, before posting the code let me post the paragraph from the kotlin official website.
A backing field will be generated for a property if it uses the
default implementation of at least one of the accessors, or if a
custom accessor references it through the field identifier.
Now here is the code below.
class Employee{
var data: String // because there are default implementation of get set
// so there will be a back-end field.
}
So I have to initialize it else compilation error.
Ok I can understand it as that some one can access it so there will be no value which can produce the wrong result.
Then I move next to understand it more, so I add custom getter.
class Employee{
var data: String
get() = "default value"
}
This also generate the back-end field so compilation error to initialize it. I can understand it as that there is no initialized value so compiler complain about it.
May be compiler is not smart enough yet to check that there is value which is giving result for this property by custom getter so don't complain about initializing just return that value when required.
But there should be not a problem if any one access it then a default value is already there, then why compiler still complain?
Then I move one step more to implement custom setter too.
class Employee{
var data: String
get() = "default value"
set(value){
field = value
}
}
Still there is the back-end field because we have accessed the field so compiler generate the back-end field.
Same error, should be initialized.
Then the final stage where it works fine as below.
class Employee{
var data: String
get() = "default value"
set(value){
}
}
Now I'm not accessing field in custom getter setter so there is not a back-end field. And it works fine.
So the final question when the property should be intialized? When there is a back-end field generated?
Yes this does not compile:
class Employee{
var data: String
get() = "default value"
}
but this does:
class Employee{
val data: String
get() = "default value"
}
so maybe the compiler by stating Property must be initialized for the wrong declaration, wants from you to admit that data is something that you can not change. I say maybe.
Now the part that does compile:
class Employee{
var data: String
get() = "default value"
set(value){
}
}
This is where you explicitly admit that whatever happens I will never set a value to data, and that's why the compiler feels fine.
Just to save you from more confusion, there's a lot of explaining about Kotlin in the Internet and you may find it very difficult to get familiarized with this relatively new language, but keep in mind that everything needs to be tested by you.
I found the below code in a web page:
class User{
var firstName : String
get() = field
set(value) {field = value}
var lastName : String
get() = field
set(value) {field = value}
}
and it is presented as compilable when it's not.
You kind of answered your own question. There's no backing field when you override both getter and setter, and don't access field.
About your "compiler not being smart enough": get() function is actually RAN at runtime, so writing a lot of compiler code just to evaluate if return value is static and should be injected as default is too niche of a use case.
If your getter depends on another field which is only initialized later, this would cause a lot of confusion as to what default value should be.
Consider this code, assuming value of provider is not defined:
var data: String
get() = provider.data
What would be a default value? Do you want a null? Empty string? Maybe entire object initialization should crash? Explicit default value declaration is needed for that purpose.
That's where idea of lateinit var came to be: if You're certain you will set value before performing any get, You can use this keyword to prevent compiler errors and setting default value.
class Employee{
var data: String
get() = "default value"
}
var means there are both a getter and a setter. Because you didn't write a setter, you get the default one, which accesses the backing field. So there is a backing field, and it needs to be initialized.
But there should be not a problem if any one access it then a default value is already there, then why compiler still complain?
Because that makes the rules simpler: all properties with backing fields must be initialized. This in turn may be because in Java fields don't have to be initialized and this is a known source of bugs. I would like to say it also avoids a possible bug, because presumably you don't actually want the setter's result never to be accessible, but initializing doesn't fix that problem.
I don't see any obvious problem with changing the rules so that a field only needs to be initialized when accessed in the getter, and maybe adding a warning when only one accessor uses field. But I may be missing something, and don't see much benefit to doing so either.