Can't use observableArrayList as value of hashmap in kotlin - kotlin

I am trying to create a hashmap of a generic type to an array list type from FX Collections.
private val items = FXCollections.observableArrayList<T>()
private val itemsData = hashMapOf<T, FXCollections.observableArrayList<ItemData<T>>>()
The first line works fine, the second gives me a red line under 'observableArrayList'.
Unresolved reference: observableArrayList
This also works fine:
private val itemsData = hashMapOf<T, ItemData<T>>()
I'm new to kotlin and javafx, but even if importing observableArrayList directly doesn't help..

You're confusing a type with an object.
FXCollections.observableArrayList is a method that returns an instance of type ObservableList<E>. The declaration of the HashMap needs a type in the generics though.
Give this a try:
val itemsData = hashMapOf<T, ObservableList<ItemData<T>>>();
A simpler example as an explaination:
// delcare that my password storage has a string type as key and a string type as value
val myPasswords = hashMapOf<String, String>();
// add a pair of string instances
myPasswords["stackoverflow.com"] = "topsecret"

Related

declare global variable in kotlin defaulted to a property value

I am trying to load a global variable in kotlin directly from the application.yml:
telegram:
token: foo
to achieve this, in my class I've tried this:
#Value("\${telegram.token}")
val botToken: String
But it is throwing an error saying that I need to initialize the property. (For example, this doesn't throw an error but it is not my expected behaviour):
#Value("\${telegram.token}")
val botToken: String = ""
What I want is to inject the config value (foo) into this constant (botToken).
Either add it as a parameter in the constructor of the bean that contains the property:
class WhateverSpringManagedBeanClass(
#Value("\${telegram.token}") private val botToken: String
)
Or try the following (this makes botToken mutable):
#Value("\${telegram.token}")
lateinit var botToken: String
first of all it seems like you could use #Property(name = "telegram.token")
and then i would attempt
#Property(name = "telegram.token")
lateinit var token: String
private set

How parse raw json list of data posted in ktor

I'm posting json array of objects. I'm trying to parse it in code like this
val objs = call.receive<List<MyClass>>() // this work fine
val name objs[0].name // this throw exception LinkedTreeMap cannot be cast to MyClass
In above code second line throws exception com.google.gson.internal.LinkedTreeMap cannot be cast to MyClass
If i post simple object and parse it in ktor with call.receive<MyClass>() then it will work fine. So issue is only when parsing list of objects.
Using your code with Array instead of List worked for me using ktor v1.2.3:
val objs = call.receive<Array<MyClass>>()
val name = objs[0].name
Side Note:
I later changed my data class to this format to help with mapping from database rows to a data class (i.e. to use BeanPropertyRowMapper). I don't remember this having an effect on de/serialization, but if the first part still isn't working for you, you may try this...
data class MyClass(
var id: Int? = null,
var name: String? = null,
var description: String? = null,
)
Reference: Kotlin data class optional variable
You can do like this
val json = call.receive<String>()
val objs = Gson().fromJson(json, Array<MyClass>::class.java)
objs[0].name
Updated
You can also create extention function for that like this
suspend inline fun <reified T> ApplicationCall.safeReceive(): T {
val json = this.receiveOrNull<String>()
return Gson().fromJson(json, T::class.java)
}
then use it like this
val objs = call.safeReceive<Array<MyClass>>()
objs[0].name

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.

DynamoDBMapper load cannot instantiate Kotlin data class

Using DynamoDBMapper within an AWS Lambda (i.e. not Android) written in Kotlin, I can save a record using a data class. However when I attempt to load a record to a data class, I receive a "DynamoDBMappingException: could not instantiate class" exception.
#DynamoDBTable(tableName = "Test")
data class TestItem(
#DynamoDBHashKey(attributeName="someKey")
#DynamoDBAttribute(attributeName = "someKey")
var someKey: String?,
#DynamoDBAttribute(attributeName = "someValue")
var someValue: String?
}
val ddbMapper = DynamoDBMapper(AmazonDynamoDBClientBuilder.defaultClient())
ddbMapper.load(TestItem::class.java, "xyz")
Results in the following exception:
com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException:
could not instantiate class
com.intuit.connect_to_pro.lambda_common_core.aws_service.TestItem
With the root exception being:
java.lang.NoSuchMethodException:
com.intuit.connect_to_pro.lambda_common_core.aws_service.TestItem.()
AWS has an example for Android that uses com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBMapper instead of com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper. I tried the Android version, but the result was the same.
https://docs.aws.amazon.com/aws-mobile/latest/developerguide/add-aws-mobile-nosql-database.html
Any help would be appreciated.
The DynamoDBMapper expects a class with an empty constructor. Using a Kotlin data class, you can specify default values for all parameters and use #JvmOverload, which will generate the empty constructor for JVM (Java). Also all parameters need to be mutable, so you need to use "var" instead of "val".
#DynamoDBTable(tableName = "Test")
data class TestItem #JvmOverloads constructor(
#DynamoDBHashKey(attributeName="someKey")
var someKey: String = "",
var someValue: String = ""
)
Make sure that all your classes have an empty constructor. In my case I had nested documents. Those had to have empty constructors too.
In Kotlin, an empty (parameterless) constructor will be created if you specify default values for all the attributes.
Also, make sure that the data from the db can be converted to the data in your classes.
For example, mine failed because I had an Integer property in my class while in the db I had a String. i.e. I had the String value "30" in the db, instead of the Integer value 30.

How to make a builder for a Kotlin data class with many immutable properties

I have a Kotlin data class that I am constructing with many immutable properties, which are being fetched from separate SQL queries. If I want to construct the data class using the builder pattern, how do I do this without making those properties mutable?
For example, instead of constructing via
var data = MyData(val1, val2, val3)
I want to use
builder.someVal(val1)
// compute val2
builder.someOtherVal(val2)
// ...
var data = builder.build()
while still using Kotlin's data class feature and immutable properties.
I agree with the data copy block in Grzegorz answer, but it's essentially the same syntax as creating data classes with constructors. If you want to use that method and keep everything legible, you'll likely be computing everything beforehand and passing the values all together in the end.
To have something more like a builder, you may consider the following:
Let's say your data class is
data class Data(val text: String, val number: Int, val time: Long)
You can create a mutable builder version like so, with a build method to create the data class:
class Builder {
var text = "hello"
var number = 2
var time = System.currentTimeMillis()
internal fun build()
= Data(text, number, time)
}
Along with a builder method like so:
fun createData(action: Builder.() -> Unit): Data {
val builder = Builder()
builder.action()
return builder.build()
}
Action is a function from which you can modify the values directly, and createData will build it into a data class for you directly afterwards.
This way, you can create a data class with:
val data: Data = createData {
//execute stuff here
text = "new text"
//calculate number
number = -1
//calculate time
time = 222L
}
There are no setter methods per say, but you can directly assign the mutable variables with your new values and call other methods within the builder.
You can also make use of kotlin's get and set by specifying your own functions for each variable so it can do more than set the field.
There's also no need for returning the current builder class, as you always have access to its variables.
Addition note: If you care, createData can be shortened to this:
fun createData(action: Builder.() -> Unit): Data = with(Builder()) { action(); build() }.
"With a new builder, apply our action and build"
I don't think Kotlin has native builders. You can always compute all values and create the object at the end.
If you still want to use a builder you will have to implement it by yourself. Check this question
There is no need for creating custom builders in Kotlin - in order to achieve builder-like semantics, you can leverage copy method - it's perfect for situations where you want to get object's copy with a small alteration.
data class MyData(val val1: String? = null, val val2: String? = null, val val3: String? = null)
val temp = MyData()
.copy(val1 = "1")
.copy(val2 = "2")
.copy(val3 = "3")
Or:
val empty = MyData()
val with1 = empty.copy(val1 = "1")
val with2 = with1.copy(val2 = "2")
val with3 = with2.copy(val3 = "3")
Since you want everything to be immutable, copying must happen at every stage.
Also, it's fine to have mutable properties in the builder as long as the result produced by it is immutable.
It's possible to mechanize the creation of the builder classes with annotation processors.
I just created ephemient/builder-generator to demonstrate this.
Note that currently, kapt works fine for generated Java code, but there are some issues with generated Kotlin code (see KT-14070). For these purposes this isn't an issue, as long as the nullability annotations are copied through from the original Kotlin classes to the generated Java builders (so that Kotlin code using the generated Java code sees nullable/non-nullable types instead of just platform types).