Trying to implement the TornadoFX "Motivational example" - kotlin

I'm trying to implement the motivational example from this page: https://docs.tornadofx.io/0_subsection/1_why_tornadofx
For this I need a data class Person as defined here:
class Person(id: Int, name: String, birthday: LocalDate) {
val idProperty = SimpleIntegerProperty(id)
var id by idProperty
val nameProperty = SimpleStringProperty(name)
var name by nameProperty
val birthdayProperty = SimpleObjectProperty(birthday)
var birthday by birthdayProperty
val age: Int get() = Period.between(birthday, LocalDate.now()).years
}
To do this it was neccessary to make the following imports:
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import java.time.LocalDate
import java.time.Period
However, if I try to run the example I get the following error:
Kotlin: Property delegate must have a 'getValue(Person, KProperty<*>)' method. None of the following functions is suitable:
public open fun getValue(): Int! defined in javafx.beans.property.SimpleIntegerProperty
I can circumvent this by not using delegate types and setting the properties like this:
val idProperty = SimpleIntegerProperty(id)
var id: Int
get() = idProperty.value
set(value) { idProperty.value = value}
But that seems to defeat the point of using delegates in TornadoFX when this is their motivational example for using it.
Here's what I found on delegate types: https://edvin.gitbooks.io/tornadofx-guide/content/part2/Property_Delegates.html
That doesn't help with getting the shorthand of var id by idProperty to work though.
Can somebody point me in the right direction here?

You need to also import the following:
import tornadofx.getValue
import tornadofx.setValue
Those are extension operator functions defined for various types in JavaFX (e.g., properties, observable values, etc.) so that those types can be used as delegates. But those function aren't defined in those types, thus the need for the additional imports.

Related

Official documentation code gives `Type 'KProperty0<Int>' has no method 'getValue(MyClass, KProperty<*>)' and thus it cannot serve as a delegate`

Did copy/paste from official documentation:
https://kotlinlang.org/docs/delegated-properties.html#delegating-to-another-property
var topLevelInt: Int = 0
class ClassWithDelegate(val anotherClassInt: Int)
class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) {
var delegatedToMember: Int by this::memberInt
var delegatedToTopLevel: Int by ::topLevelInt
val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt
}
var MyClass.extDelegated: Int by ::topLevelInt
And there is an error:
I think I need some packages imported, like this answer does, but for Intellij, not Jetpack Compose : https://stackoverflow.com/a/63877349/10777336
The code from the documentation only works for Kotlin version 1.4+. From What's New 1.4:
Better inference for delegated properties
The type of a delegated property wasn’t taken into account while analyzing the delegate expression which follows the by keyword. For instance, the following code didn’t compile before, but now the compiler correctly infers the types of the old and new parameters as String?:
import kotlin.properties.Delegates
fun main() {
var prop: String? by Delegates.observable(null) { p, old, new ->
println("$old → $new")
}
prop = "abc"
prop = "xyz"
}
So you should just update your Kotlin version to 1.4+.

Why can Kotlin's code directly call the top level function from “kotlin.collections”, without import the package

Why can Kotlin's code directly call the top level function from “kotlin.collections”, without import the package. such as below function listOf:
data class Person1(val name: String, val age: Int)
class DataClassExecutor {
... ...
fun test(arg: String?): String? {
val persons = listOf(
Person1("Lucy", age = 26),
Person1("Lily", age = 29))
... ...
}
}
Please refer this page: https://kotlinlang.org/spec/packages-and-imports.html. It says:
There are some packages which have all their entities implicitly
imported into any Kotlin file, meaning one can access such entity
without explicitly using import directives.
The List includes kotlin.collections.
This is similar to how in Java, java.lang is implicitly imported. In Java one does not need to say java.lang.System.out.println, just System.out.println is enough.

ArrayList<AbstractObject> adding objects which extended AbstractObject is not possible. How to fix it?

So I have ArrayList<AbstractObject> which is class type abstract. And I have 2 items which extends AbstractObject. If I use abstractList.add(Object1) it says that ArrayList expects object of type AbstractObject and not Object1. I thought that this is possible. Reason why I want to do this is to use multiple objects with 2 different data in single RecyclerView. (ViewTypes)
abstract class ListItem {
abstract val type: Int
companion object {
const val TYPE_HEADER = 0
const val TYPE_ITEM = 1
}
}
class HeaderItem(val headerTitle: String) : ListItem() {
val type: Int
get() = TYPE_HEADER
}
class ObjectItem(val object: ParseObject) : ListItem() {
val type: Int
get() = TYPE_ITEM
}
Init #1:
var recyclerViewArray: ArrayList<out ListItem> = ArrayList()
This is error if I want to add HeaderItem to this list:
Init #2:
var recyclerViewArray: ArrayList<ListItem> = ArrayList()
This says Type mismatch. Tried with as but as is yellowed with message This cast can never succeed.
It is possible and it works fine. If in some expression Kotlin infers the type wrong, you can always specify it manually. In your case
abstractList.add(Object1 as AbstractObject)
Remove out from the declaration of recyclerViewArray and it should work (I just tried it and it ran fine). e.g. I could run this line of code:
recyclerViewArray.add(HeaderItem("test"))
Note that when you remove out you still need to keep the fact that it's an ArrayList of ListItem objects. So you should declare it as:
var recyclerViewArray = arrayListOf<ListItem>()
Some of your code didn't quite compile for me, like having a property called object (I had to put backticks around that) and not putting the override modifier on the type property on HeaderItem.

Global extension function in kotlin

Hey I want to make a class in kotlin that will hold all extension functions that I will use in a few places for example:
class DateUtils {
//in this case I use jodatime
fun Long.toDateTime() : DateTime = DateTime(this)
fun String.toDateTime() : DateTime = DateTime.parse(this)
}
class SomeClassWithNoConnectionToDateUtils {
fun handleDataFromServer(startDate: String) {
someOtherFunction()
//startDate knows about toDateTime function in DateUtils
startDate.toDateTime().plusDays(4)
}
}
Is there a way to perform such operation
Having your extensions inside a DateUtils class will make them available for use only inside the DateUtils class.
If you want the extensions to be global, you can just put them on the top level of a file, without putting them inside a class.
package com.something.extensions
fun Long.toDateTime() : DateTime = DateTime(this)
fun String.toDateTime() : DateTime = DateTime.parse(this)
And then import them to use them elsewhere like so:
import com.something.extensions.toDateTime
val x = 123456L.toDateTime()

Exception when using spring-data-mongodb with Kotlin

I'm new to Kotlin, and experimenting with spring-data-mongodb. Please see example below (also available here as fully runnable Maven project with in-memory MongoDb: https://github.com/danielsindahl/spring-boot-kotlin-example).
Application.kt
package dsitest
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
#SpringBootApplication
open class Application
fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
User.kt
package dsitest
import org.springframework.data.annotation.Id
import org.springframework.data.annotation.PersistenceConstructor
import org.springframework.data.mongodb.core.mapping.Document
#Document(collection = "user")
data class User #PersistenceConstructor constructor(#Id val id: String? = null, val userName: String)
UserRepository.kt
package dsitest
import org.springframework.data.repository.CrudRepository
interface UserRepository : CrudRepository<User, String>
KotlinIntegrationTest.kt
package dsitest
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner
#RunWith(SpringRunner::class)
#SpringBootTest
class KotlinIntegrationTest constructor () {
#Autowired
lateinit var userRepository : UserRepository;
#Test
fun persistenceTest() {
val user : User = User(userName = "Mary")
val savedUser = userRepository.save(user)
val loadedUser = userRepository.findOne(savedUser.id) // Failing code
println("loadedUser = ${loadedUser}")
}
}
When running the test KotlinIntegrationTest.persistenceTest, I get the following error message when trying to retrieve a User object from MongoDb:
org.springframework.data.mapping.model.MappingException: No property null found on entity class dsitest.User to bind constructor parameter to!
If I modify the User data class so that userName is nullable, everything works.
data class User #PersistenceConstructor constructor(#Id val id: String? = null,
val userName: String? = null)
I would like to understand why this is the case, since I don't want userName to be nullable. Is there some alternative, better way of defining my User class in Kotlin?
Many thanks,
Daniel
Yes, it is a known problem. You should check how the bytecode for your User class looks like. Java sees the constructor with all the parameters present and tries to call it with a null value for the 2nd one.
What you could do is to try adding #JvmOverloads to your constructor - this will force Kotlin compiler to generate all versions of the constructor and so the Spring Data Mongo could pick the correct one (get rid of the #PersistenceConstructor) then.
You could also define 1 constructor with no defaults - only for Java-based frameworks and 2nd one with some defaults your you. Or...
When I write things like you are now, I create simple 'persistence' data classes with no default values whatsoever that are mapped to/from my regular domain objects (a sort of abstraction over database). It may generate some overhead at the start - but keeping your domain model not coupled so tightly with the storage model is usually a good idea anyway.