More elegant way to call getter within deeply nested data classes - kotlin

I have deserialized a huge XML file and that leaves me with a lot of (nested) data classes. I'm looking for a more elegant way to do calls to specific getters within these objects. So I would rather not fill my code with all these dot calls.
I have to set quite some fields to return a new Model, which currently looks something like this (I made up the model for this question):
return TestClass(
appointmentDate = message.data.appointment.appointmentDescription.AppointmentDetails.appointmentDate
appointmentX = message.data.appointment.appointmentDescription.X.Y.Z
appointmentY = message.data.appointment.appointmentDescription.AppointmentDetails.X.Y.Z
appointmentZ = message.data.appointment.Z
)
I know I could do val appointmentDescription = message.data.appointment.appointmentDescription and do my calls from there to shorten it a bit, but there must be a shorter and better way I assume.
Does anyone know a way to do this in a more elegant and clean way? I'm pretty new to Kotlin and it is quite hard for me to properly lookup any solutions on the internet since I can't find any and am probably searching for the wrong things.

Try the with function:
data class Message (val data: Data)
data class Data (val appointment: Appointment)
data class Appointment(val appointmentDescription: AppointmentDescription)
data class AppointmentDescription(val x: Int, val appointmentDetails: AppointmentDetails)
data class AppointmentDetails(val x:Int, val y:Int)
fun TestClass(message: Message) {
with(message.data.appointment.appointmentDescription) {
val appointmentX = x
val appointmentY = appointmentDetails.y
}
}
This should be useful: https://medium.com/mobile-app-development-publication/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84

Related

Class method signature based on supplied type in Kotlin?

I'm trying to reduce boilerplate on something I'm working on and wondering if something is possible - I suspect it's not but was looking for confirmation
class Something<T> {
private val list = mutableListOf<T>()
fun addToList(value: T) = list.add(value) }
So if I wanted to use this with a class like:
class Data(number: Int, letter: Char)
I'd have to use addToList like:
addToList(Data(1,"a"))
Is there some way to use the supplied type T to construct the method addToList dynamically? So that the class would be instantiated like:
val thing = Something<Data>()
but then addToList were called like
addToList(1,"a")
Like I said, don't think this is possible but was looking for confirmation.
What I was really trying to do was come up with something that would allow me to do this without declaring Data at all, but instead just define the structure and the subsequent addToList method when Something() was instantiated - not sure if I have described this all that well but if anyone has any suggestions in general around that I'd be grateful!
Thanks!
There are Pair and Triple tuple classes provided in the standard library which allows you to avoid declaring a class for simple combinations of values. If you need more than 3 parameters of different types, you'd need to create your own class or use a library that provides larger tuple classes. If all types are the same, you can use List instead of a tuple.
In my opinion even Triple is pushing it and anything with more than two distinct properties should just have its own data class defined.
class Something<A, B> {
private val list = mutableListOf<Pair<A, B>>()
fun addToList(valueA: A, valueB: B) = list.add(Pair(valueA, valueB))
}
val something = Something<Int, String>()
something.addToList(1, "a")
An alternate approach if you want to keep the flexibility of your Something class to hold anything would be to use an extension function.
class Something<T> {
private val list = mutableListOf<T>()
fun addToList(value: T) = list.add(value)
}
fun <A, B> Something<Pair<A, B>>.addToList(valueA: A, valueB: B) =
addToList(Pair(valueA, valueB))
val something = Something<Pair<Int, String>>()
something.addToList(1, "a")

Is there a way to make this Kotlin code more clean?

I have the following code but I am convinced that it could be simpler/more elegant
package org.example
import javax.enterprise.context.ApplicationScoped
#ApplicationScoped
object CanceledRequestsHandler {
var identifiers = setOf<String>()
fun add(id: String){
var mutableIdentifiers = identifiers.toMutableList()
mutableIdentifiers.add(id)
this.ids = mutableIds.toSet()
}
}
I wanted to limit the mutability. Any suggestions t improve my code?
There is a more elegant way! Try this script file (.kts) with kotlinc (or you can run it within IDEA):
object CanceledRequestsHandler {
var ids = setOf<String>()
override fun toString(): String = ids.toString()
fun add(id: String){
ids = ids + id
}
}
System.err.println(CanceledRequestsHandler);
CanceledRequestsHandler.add("foo");
CanceledRequestsHandler.add("bar");
System.err.println(CanceledRequestsHandler);
A bit of explanation:
The + operator can be applied to collections, and works as one might expect -- returns a new collection (see https://kotlinlang.org/docs/reference/collection-plus-minus.html).
I'm not sure what your separate identifiers variable was, but you can use a single var that contains an immutable Set to do your job here.
As a commenter and the accepted answer point out, there's more you can do. If you're trying to limit mutability entirely to add(), you can lock this down further, with something like this:
object CanceledRequestsHandler {
private var _ids = mutableSetOf<String>()
val ids
get() = _ids.toSet()
override fun toString(): String = ids.toString()
fun add(id: String) {
_ids.add(id)
}
}
System.err.println(CanceledRequestsHandler);
CanceledRequestsHandler.add("foo");
CanceledRequestsHandler.add("bar");
System.err.println(CanceledRequestsHandler);
// Below line does not compile
// CanceledRequestsHandler.ids.add("baz")
Now the only way the ids property can change is via the add() method.
I wanted to limit the mutability
Converting immutable collection to mutable on each data mutation is not a limitation of mutablility, it's just overhead. The worst here is that property is declared as mutable (var). This design may lead to data loss in multi-thread case.
If data mutation is unavoidable, then it's better to have mutable (concurrent in multi-thread case) data collection with immutable property (val).
Even better way to limit mutability will be using a mutable data structure only for a short initialization period, and then freezing it into immutable (see buildSet), but I'm not sure that this approach is applicable in your case.

Kotlin multiple class for data storage

I am developing a simple Android app, that will display an icon of a vehicle and the user can click on the icon to display the vehicle information. I want to load the data dynamically when I build the app i.e. the data will come from an external source including the picture for the icon.
I am new to Kotlin and not sure what to search for to understand a suitable solution. What is the correct way to define the data, is it best to create an class as below then create an array of the class (not sure if this is possible)
public class VehicleSpec()
{
var OEM: String? = null
var ModelName: String? = null
var EngineSize: String? = null
}
Or would be better to create a multiple dimension array and then link the data to the cells?
var VehicleSpec = arrayOf(20,20)
VehicleSpec[0][0] = Null //OEM
VehicleSpec[0][1] = Null //ModelName
VehicleSpec[0][2] = Null //EngineSize
What is the best way to set up the data storage, is there any good references to understand how this should be setup?
What is the correct way to define the data, is it best to create an class as below then create an array of the class
Using an array for the properties of an object is not making the full use of the type safety you have in Kotlin (and even Java for that matter).
If what you want to express is multiple properties of an object, then you should use a class to define those properties. This is especially true if the properties have different types.
There is no performance difference between an array and a class, because you'll get a reference to the heap in both cases. You could save on performance only if you convert your multi-dimensional array approach to a single-dimension array with smart indexing. Most of the time, you should not consider this option unless you are handling a lot of data and if you know that performance is an issue at this specific level.
(not sure if this is possible)
Defining lists/arrays of classes is definitely possible.
Usually, for classes that are only used as data containers, you should prefer data classes, because they give you useful methods for free, and these methods totally make sense for simple "data bags" like in your case (equals, hashcode, component access, etc.).
data class Vehicle(
val OEM: String,
val ModelName: String,
val EngineSize: String
)
Also, I suggest using val instead of var as much as possible. Immutability is more idiomatic in Kotlin.
Last but not least, prefer non-null values to null values if you know a value must always be present. If there are valid cases where the value is absent, you should use null instead of a placeholder value like empty string or -1.
First at all, using the "class aprocah" makes it easy for you to understand and give you the full benefits of the language itself... so dont dry to save data in an array .. let the compiler handle those stuff.
Secondly i suggest you have maybe two types (and use data classes ;-) )
data class VehicleListEntry(
val id: Long,
val name: String
)
and
data class VehicleSpec(
val id: Long,
val oem: String = "",
val modelName: String = "",
val engineSize: String = ""
)
from my perspective try to avoid null values whenever possible.
So if you have strings - which you are display only - use empty strings instead of null.
and now have a Model to store your data
class VehicleModel() {
private val specs: MutableMap<Long, VehicleSpec> = mutableMapOf()
private var entries: List<VehicleListEntry> = listOf()
fun getSpec(id: Long) = specs[id]
fun addSpec(spec: VehicleSpec) = specs[spec.id] = spec
fun getEntries(): List<VehicleListEntry> = entries
fun setEntries(data: List<VehicleListEntry>) {
entries = data.toMutableList()
}
}
You could also use a data class for your model which looks like
data class VehicleModel(
val specs: MutableMap<Long, VehicleSpec> = mutableMapOf(),
var entries: List<VehicleListEntry> = listOf()
)
And last but not least a controller for getting stuff together
class VehicleController() {
private val model = VehicleModel()
init{
// TODO get the entries list together
}
fun getEntries() = model.entries
fun getSpec(id: Long) : VehicleSpec? {
// TODO load the data from external source (or check the model first)
// TODO store the data into the model
// TODO return result
}
}

dataclass copy with field ArrayList - change the ArrayList of the copied class changes the original

I have a data class like this
data class Person(val id: Long = BaseDataContract.BaseData.UNDEFINED_ID.toLong(),
.....
val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>())
I have two copies of the object:
person = originalPerson.copy()
Then I change the elements of personConsents for the object person - I add/delete/edit them.
But by some reason I see that the same changes are happening in originalPerson object which I don't want to be. originalPerson is not supposed to be changed at all.
Suspect there is something with ArrayList references, but need your advice what i can do? At the end I need to compare two objects likefun dataChanged(): Boolean = originalPerson != person bu it doesn't work when ArrayList is changing.
I found a simple solution. I use my own clone function which creates a new object for ArrayList and fill it by copied elements.
fun getPersonClone(person: Person): Person {
val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>()
person.personConsents.forEach { personConsents.add(it.copy()) }
return Person(person.id, ......., personConsents)
}
So,this link here, will help you understand that the copy method in Kotlin, does not do a deep copy, it only does a shallow one. This is specially seen with non-primitive data types such as the ArrayList one you're using.
If you must use a method to copy the data class directly, what you can do is serialize it, and then de-serialize it.
I've done something like this below, using Gson.
Using the Data Class
data class Person(var name: String? = null,val cars : ArrayList<String> = ArrayList() )
The main method
fun main (args: Array<String>) {
var original =Person("Jon", arrayListOf("Honda City","VW Vento"))
var clone =Gson().fromJson(Gson().toJson(original), Person::class.java)
original.cars.add("VW Polo")
original.name = "Henry"
print(clone.cars) // Prints [Honda City, VW Vento]
}
This approach seems really hacky, and I'd encourage anything better.
The Kotlin copy method is a shallow copy. So your copy ends up referencing the exact same array as the original object.
I would say the simplest way to fix this is to implement the copy method yourself (no need to create an extra custom clone method):
data class Person(val id: Long = BaseDataContract.BaseData.UNDEFINED_ID.toLong(), val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>()) {
fun copy() = Person(this.id, ArrayList(this.personConsents))
}

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).