ArrayList cannot be cast to java.lang.Object[] - kotlin

I used one of the answers from this question to get help with previous error but now I'm getting another one. There's a suggested answer to this question but I'm unable to get solution out of it for my problem.
java.lang.ClassCastException: java.util.ArrayList cannot be cast to
java.lang.Object[]
private var data: Any? // fixed, can't change data type as it's in a compiled library to accept all kinds of data.
fun users() : ArrayList<User> {
return (data as Array<*>).filterIsInstance<User>() as ArrayList<User>
}
After the suggestions in the comment, the working code looks like this but I've another side effect, I can't add items to the array, the ArrayList remains empty.
fun users() : ArrayList<User> {
return (data as ArrayList<*>).filterIsInstance<User>() as ArrayList<User>
}
fun addItem(userVO: User) {
users().add(user)
}
Edit 2
val users: ArrayList<User> get() = ((data as? ArrayList<Any>)?.filterIsInstance<User>() ?: emptyList()) as ArrayList<User>
fun addItem(user: User) {
users.add(user)
}

Having untyped data as a source is not the best idea but if you are working with a third-party library you might have no choice. In that case, you may try the following:
private var data: Any? = null
#Suppress("UNCHECKED_CAST")
val users: List<User> get() = (data as? ArrayList<Any>)?.filterIsInstance<User>() ?: emptyList()
fun addUser(user: User) {
#Suppress("UNCHECKED_CAST")
(data as? ArrayList<Any>)?.add(user)
}
In the above, I suppose that the data list may contain not only users but other entities also.

Related

What is the type of a Kotlin 'data class'?

I have a situation where I need to create a copy of data class object. I don't know in advance which of the many data classes I have will come in into the function. I do know, however, that only data classes will be used as input to this function.
This is what didn't work:
fun doSomething(obj: Any): Any {
obj.copy(...) // <- there's no 'copy' on Any
...
}
This is what I really like to do:
fun doSomething(obj: KAnyDataClass): KAnyDataClass {
obj.copy(...) // <- works, data classes have a 'copy' method
...
}
I'm not a Kotlin developer, but it looks like the language does not support dynamic dispatch or traits. You might find success with the dynamic type, which just turns off the type-checker so it won't yell at you for using a method that it doesn't know about. However this opens up the possibility of a runtime error if you pass an argument that actually doesn't have that method.
There is no class or interface for data classes, but we know from the documentation of data classes that there are derived functions componentN and copy in each data class.
We can use that knowledge to write an abstract copy method that calls the copy method of a given arbitrary data class using reflection:
fun <T : Any> copy(data: T, vararg override: Pair<Int, Any?>): T {
val kClass = data::class
if (!kClass.isData) error("expected a data class")
val copyFun = kClass.functions.first { it.name == "copy" }
checkParameters(override, kClass)
val vals = determineComponentValues(copyFun, kClass, override, data)
#Suppress("UNCHECKED_CAST")
return copyFun.call(data, *vals) as T
}
/** check if override of parameter has the right type and nullability */
private fun <T : Any> checkParameters(
override: Array<out Pair<Int, Any?>>,
kClass: KClass<out T>
) {
override.forEach { (index, value) ->
val expectedType = kClass.functions.first { it.name == "component${index + 1}" }.returnType
if (value == null) {
if (!kClass.functions.first { it.name == "component${index + 1}" }.returnType.isMarkedNullable) {
error("value for parameter $index is null but parameter is not nullable")
}
} else {
if (!expectedType.jvmErasure.isSuperclassOf(value::class))
error("wrong type for parameter $index: expected $expectedType but was ${value::class}")
}
}
}
/** determine for each componentN the value from override or data element */
private fun <T : Any> determineComponentValues(
copyFun: KFunction<*>,
kClass: KClass<out T>,
override: Array<out Pair<Int, Any?>>,
data: T
): Array<Any?> {
val vals = (1 until copyFun.parameters.size)
.map { "component$it" }
.map { name -> kClass.functions.first { it.name == name } }
.mapIndexed { index, component ->
override.find { it.first == index }.let { if (it !== null) it.second else component.call(data) }
}
.toTypedArray()
return vals
}
Since this copy function is generic and not for a specific data class, it is not possible to specify overloads in the usual way, but I tried to support it in another way.
Let's say we have a data class and element
data class Example(
val a: Int,
val b: String,
)
val example: Any = Example(1, "x")
We can create a copy of example with copy(example) that has the same elements as the original.
If we want to override the first element, we cannot write copy(example, a = 2), but we can write copy(example, 0 to 2), saying that we want to override the first component with value 2.
Analogously we can write copy(example, 0 to 3, 1 to "y") to specify that we want to change the first and the second component.
I am not sure if this works for all cases since I just wrote it, but it should be a good start to work with.

Implementing observable properties that can also serialize in Kotlin

I'm trying to build a class where certain values are Observable but also Serializable.
This obviously works and the serialization works, but it's very boilerplate-heavy having to add a setter for every single field and manually having to call change(...) inside each setter:
interface Observable {
fun change(message: String) {
println("changing $message")
}
}
#Serializable
class BlahVO : Observable {
var value2: String = ""
set(value) {
field = value
change("value2")
}
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
println(BlahVO().apply { value2 = "test2" })
correctly outputs
changing value2
{"value2":"test2"}
I've tried introducing Delegates:
interface Observable {
fun change(message: String) {
println("changing $message")
}
#Suppress("ClassName")
class default<T>(defaultValue: T) {
private var value: T = defaultValue
operator fun getValue(observable: Observable, property: KProperty<*>): T {
return value
}
operator fun setValue(observable: Observable, property: KProperty<*>, value: T) {
this.value = value
observable.change(property.name)
}
}
}
#Serializable
class BlahVO : Observable {
var value1: String by Observable.default("value1")
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
println(BlahVO().apply { value1 = "test1" }) correctly triggers change detection, but it doesn't serialize:
changing value1
{}
If I go from Observable to ReadWriteProperty,
interface Observable {
fun change(message: String) {
println("changing $message")
}
fun <T> look(defaultValue: T): ReadWriteProperty<Observable, T> {
return OP(defaultValue, this)
}
class OP<T>(defaultValue: T, val observable: Observable) : ObservableProperty<T>(defaultValue) {
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
super.setValue(thisRef, property, value)
observable.change("blah!")
}
}
}
#Serializable
class BlahVO : Observable {
var value3: String by this.look("value3")
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
the result is the same:
changing blah!
{}
Similarly for Delegates.vetoable
var value4: String by Delegates.vetoable("value4", {
property: KProperty<*>, oldstring: String, newString: String ->
this.change(property.name)
true
})
outputs:
changing value4
{}
Delegates just doesn't seem to work with Kotlin Serialization
What other options are there to observe a property's changes without breaking its serialization that will also work on other platforms (KotlinJS, KotlinJVM, Android, ...)?
Serialization and Deserialization of Kotlin Delegates is not supported by kotlinx.serialization as of now.
There is an open issue #1578 on GitHub regarding this feature.
According to the issue you can create an intermediate data-transfer object, which gets serialized instead of the original object. Also you could write a custom serializer to support the serialization of Kotlin Delegates, which seems to be even more boilerplate, then writing custom getters and setters, as proposed in the question.
Data Transfer Object
By mapping your original object to a simple data transfer object without delegates, you can utilize the default serialization mechanisms.
This also has the nice side effect to cleanse your data model classes from framework specific annotations, such as #Serializable.
class DataModel {
var observedProperty: String by Delegates.observable("initial") { property, before, after ->
println("""Hey, I changed "${property.name}" from "$before" to "$after"!""")
}
fun toJson(): String {
return Json.encodeToString(serializer(), this.toDto())
}
}
fun DataModel.toDto() = DataTransferObject(observedProperty)
#Serializable
class DataTransferObject(val observedProperty: String)
fun main() {
val data = DataModel()
println(data.toJson())
data.observedProperty = "changed"
println(data.toJson())
}
This yields the following result:
{"observedProperty":"initial"}
Hey, I changed "observedProperty" from "initial" to "changed"!
{"observedProperty":"changed"}
Custom data type
If changing the data type is an option, you could write a wrapping class which gets (de)serialized transparently. Something along the lines of the following might work.
#Serializable
class ClassWithMonitoredString(val monitoredProperty: MonitoredString) {
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
fun main() {
val monitoredString = obs("obsDefault") { before, after ->
println("""I changed from "$before" to "$after"!""")
}
val data = ClassWithMonitoredString(monitoredString)
println(data.toJson())
data.monitoredProperty.value = "obsChanged"
println(data.toJson())
}
Which yields the following result:
{"monitoredProperty":"obsDefault"}
I changed from "obsDefault" to "obsChanged"!
{"monitoredProperty":"obsChanged"}
You however lose information about which property changed, as you don't have easy access to the field name. Also you have to change your data structures, as mentioned above and might not be desirable or even possible. In addition, this work only for Strings for now, even though one might make it more generic though.
Also, this requires a lot of boilerplate to start with. On the call site however, you just have to wrap the actual value in an call to obs.
I used the following boilerplate to get it to work.
typealias OnChange = (before: String, after: String) -> Unit
#Serializable(with = MonitoredStringSerializer::class)
class MonitoredString(initialValue: String, var onChange: OnChange?) {
var value: String = initialValue
set(value) {
onChange?.invoke(field, value)
field = value
}
}
fun obs(value: String, onChange: OnChange? = null) = MonitoredString(value, onChange)
object MonitoredStringSerializer : KSerializer<MonitoredString> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MonitoredString", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: MonitoredString) {
encoder.encodeString(value.value)
}
override fun deserialize(decoder: Decoder): MonitoredString {
return MonitoredString(decoder.decodeString(), null)
}
}

Duplicate code due to different list objects

So I got a function in each of my classes which does the same but with diffrent objects in the list, for example I got the methode for Streets and HouseNumbers.
Here the example of the two nearly identical functions, first for Streets:
fun batchInsert(import: List<ImportStreet>, source: String) {
var part : MutableList<ImportStreet> = mutableListOf()
for(i in import)
{
part.add(i)
if(part.size % 25000 == 0)
{
batchUpdate(part, source)
part = mutableListOf()
}
}
if (part.size < 25000) {
batchUpdate(part, source)
}
}
Nearly the same but now for HouseNumbers:
fun batchInsert(import: List<ImportHouseNumber>, source: String) {
var part : MutableList<ImportHouseNumber> = mutableListOf()
for(i in import)
{
part.add(i)
if(part.size % 25000 == 0)
{
batchUpdate(part, source)
part = mutableListOf()
}
}
if (part.size < 25000) {
batchUpdate(part, source)
}
}
Is there a easy or efficent way to get rid of the duplicate code?
Using Kotlin Generics - generic functions, we can create a single function that will work with both of your classes. Any class actually, as long as we are not looking to access class specific functions.
fun <T> batchInsert(import: List<T>, source: String) {
var part: MutableList<T> = mutableListOf()
for (i in import) {
part.add(i)
if (part.size % 25000 == 0) {
batchUpdate(part, source)
part = mutableListOf()
}
}
if (part.size < 25000) {
batchUpdate(part, source)
}
}
I don't know what your batchUpdate fun does, or how it does it, but it can similarly be changed in the same manner:
fun <T> batchUpdate(batch: List<T>, source: String) {
//I don't know what this function does
}
We could also take advantage of Kotlin's chunked function and make the whole process a bit more efficient (we don't need to create a new list ourselfs).
fun <T> batchInsert(import: List<T>, source: String) {
import
.chunked(25000)
.forEach { currentBatch ->
batchUpdate(currentBatch, source)
}
}
Another "trick" that we can use would be to make the fun a Kotlin extension function (note it doesn't always make sense to use this, it depends on the rest of the project, I'll recommend that you read the docs from the link)
fun <T> List<T>.batchInsert(source:String){
chunked(25000)
.forEach { currentBatch ->
batchUpdate(currentBatch, source)
}
}
Which can be called like this:
fun main() {
val list = listOf<ImportHouseNumber>() //empty list just to show how the extension works
val secondList = listOf<ImportStreet>() //empty list just to show how the extension works
val source = "source"
list.batchInsert(source)
secondList.batchInsert(source)
}
A quick and dirty solution for the case when batchUpdate can't be a generic (see comments), would be to still have a generic fun between them that will reroute each class into a different direction. Something like this:
fun <T> batchUpdate(batch: List<T>, source: String) {
val first = batch.firstOrNull() ?: return
when (first) {
is ImportStreet -> { /* do stuff with the batch and source */ }
is ImportHouseNumber -> { /* do stuff with the batch and source */ }
}
}
Note: I'm not a big fan of doing things like this, and it is a code smell. But in some cases it is enough to get through an issue. If anybody else has other ideas please advise.

Is there a simple way to get a object by _id in Kotlin?

I'm a beginner of Kotlin, I hope to get a object by _id in the following data structure, so I write the fun getMDetailByID(aMDetailsList:MDetailsList, _id:Long)... which is a traditional method.
But I think the fun is too complex, is there a simple way do that? such as use Lambda expression.
class UIMain : AppCompatActivity() {
data class BluetoothDef(val Status:Boolean=false)
data class WiFiDef(val Name:String, val Status:Boolean=false)
data class MDetail (
val _id: Long,
val bluetooth: BluetoothDef,
val wiFi:WiFiDef
)
data class MDetailsList(val mListMetail: MutableList<MDetail>)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_main)
var mBluetoothDef1=BluetoothDef()
var mWiFiDef1=WiFiDef("MyConnect 1",true)
var aMDetail1= MDetail(5L,mBluetoothDef1,mWiFiDef1)
var mBluetoothDef2=BluetoothDef(true)
var mWiFiDef2=WiFiDef("MyConnect 2")
var aMDetail2= MDetail(6L,mBluetoothDef2,mWiFiDef2)
val mListMetail:MutableList<MDetail> = mutableListOf(aMDetail1,aMDetail2)
var aMDetailsList=MDetailsList(mListMetail)
var c=getMDetailByID(aMDetailsList,5L)
}
fun getMDetailByID(aMDetailsList:MDetailsList, _id:Long):MDetail?{
var aMDetail: MDetail?=null
var a=aMDetailsList.mListMetail
for (b in a){
if (b._id==_id){
aMDetail=b
}
}
return aMDetail
}
}
A faster and simpler alternative to current answers (it's also better than your original code, since it doesn't always need to traverse the entire list):
fun getMDetailByID(aMDetailsList: MDetailsList, _id: Long) =
aMDetailsList.mListMetail.findLast { it._id == _id }
Also, consider whether you actually benefit from defining MDetailsList as a class instead of directly using (Mutable)List<MDetail> or making it a typealias. There are cases in which you do need such a type, but they aren't that common.
There is a simpler way using lambdas, indeed. You could replace your function code by:
fun getMDetailByID(aMDetailsList: MDetailsList, _id: Long): MDetail? {
return aMDetailsList.mListMetail.filter { it._id == _id }.lastOrNull()
}
It will, like your implementation did, return the last matching element or null if there is none.
Simpler code can be like:
fun getMDetailByID(aMDetailsList: MDetailsList, _id: Long) = aMDetailsList.mListMetail.filter { it._id == _id }.lastOrNull()

How to return a listmap with non null item, from a map?

I have a piece of code as below (simplified to explain the issue). From the rawData, I would like to filter out those that doesn't have a converter type provided in converter, and then for the remaining, convert the data to listitem
data class RawData(val type: String, val data: Data)
interface Converter {
fun convert(data: Data): ListItem
}
fun transform(): List<ListItem> {
val providerTypeMap = modelViewProvider.associateBy({it.type}, {it})
return rawDataList.filter {
converter[it.type] != null
}.map {
converter[it.type]?.create(it.data) ?: object: ListItem {}
}
}
Note: I want the return type as List<ListItem> and not List<ListItem?>. In order to do that, I need to have this line
converter[it.type]?.create(it.data) ?: object: ListItem {}
Which to me the ? and ?: is pretty ugly since we know by then, we already filter and only keep that converter[it.type] != null
Is there a way for me to get rid of the ? and ?: ListItem{} in my code?
There is a solution:
return rawDataList.mapNotNull {
converter[it.type]?.create(it.data)
}
But i don't know in which Kotlin's version mapNotNull() method appeared. If you haven't it you can use construction map {}.filterNotNull() or write your own mapNotNull extension method.