How to pass the current instance into the constructor of another class withon class declaration? - kotlin

I have a service that returns me a list of entities, for example:
data class TypeDto(
val type: Type,
val stages: List<StageDto>)
After applying several filter and flatMap operations, I get the desired data that has the following structure:
data class CustomerDto(
val id: String,
val name: String)
In order to extract the logic of filtering and mapping, I made a CustomerDtoWrapper class that takes List<TypeDto> as a constructor argument and does all the collection manipulation. So, in the end it looks as follows:
val types = service.getTypes()
val customers = CustomerDtoWrapper(types).filteredCustomers()
But I would like to make it more fluent and easy to read. Is it possible to call a certain function after getTypes(), so that types will be of the CustomerDtoWrapper type and look as follows:
val types = service.getTypes().someFun { ... }
val customers = types.filteredCustomers()

You can write for example extension function for this:
fun List<TypeDto>.toFilteredCustomers() = CustomerDtoWrapper(this).filteredCustomers()
and use it like this:
val customers = service.getTypes().toFilteredCustomers()

Related

Comapring data class in Kotlin using == operator

I am trying to write an assertion by comparing two Kotlin data classes.I am just simplifying the question by using a minimal class.
data class X(val isThatSo: Boolean) {
val name:String = "xyz"
}
In my test
val s = """
{
"isThatSo": "true",
"name": "Ankit"
}
"""
assert(Gson().fromJson(s, X::class.java) == X(true))
Looks to me that the name field is not compared at all because the value in both the objects is different. Is my understanding correct?
From the documentation:
data class User(val name: String, val age: Int)
The compiler automatically derives the following members from all properties declared in the primary constructor:
equals()/hashCode() pair
toString() of the form "User(name=John, age=42)"
componentN() functions corresponding to the properties in their order of declaration.
copy() function.
To exclude a property from the generated implementations, declare it inside the class body:
data class Person(val name: String) {
var age: Int = 0
}
You're comparing two instances of a class, which are not identical. You can compare the name variable within X for equality.

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

Create a new class with derived fields without duplicating field names

In Kotlin, how can I derive fields from a base definition (abstract, interface, inheritance, something else) without explicitly overriding them?
The closest I can get is:
abstract class Person {
open val name: String = "Stranger"
}
data class Doctor(
override val name: String,
val yearsOfExperience: Int
): Person()
val doc = Doctor(yearsOfExperience = 20, name = "Eric")
But ideally, since I have a use case of an unchangeable model with hundreds of fields, I would like to have:
abstract class Person {
open val name: String = "Stranger"
}
data class Doctor(
val yearsOfExperience: Int
): Person()
val doc = Doctor(yearsOfExperience = 20, name = "Eric")
You can't. If you want Doctor to be able to change Person.name to anything other than what's defined in Person, you are by definition overriding the behavior in Person - Kotlin is just forcing you to make that contract explicit.
If it didn't do that, it would be possible to do something like this:
data class Doctor(
val yearsOfExperience: Int
) : Person()
Then later decide to add a name field:
data class Doctor(
val yearsOfExperience: Int,
val name: String = "Doctor"
) : Person
Now Doctor.name has a default value of "Doctor" which is different to the expected behavior defined in Person. Previous code that did Doctor(yearsOfExperience = 20) will now behave differently - it will get the name "Doctor" instead of "Stranger". Kotlin is making sure that you realise that, and explicitly ask for it by adding the override modifier.
So you can omit the fields you want to inherit, but not the ones you want to override.

How can I create a List (or any other collection) of another class property's type dynamically?

This feature is inspired by TypeScript which allows us to create arrays based on the property of another class, whatever that property's type is.
For example assume you have this class in Kotlin:
class Person(
val name: String,
val age: Int
)
And later, somewhere else in the code I want to have a list of names, so I would do something like this:
val namesList = List<Person::name>()
And Kotlin will know that this will be equivalent to List<String>() at compile time.
This avoids me to manually propagate the type of a field I already declared in one place. Plus, if one day the name type changes from String to something else, all the collections would get updated automatically.
Can this be done in Kotlin?
No, Kotlin is very explicit about types. It is a strongly-typed language.
Maybe the closest you could do is define a type alias next to your class and use that:
typealias PersonName = String
data class Person(val name: PersonName, val age: Int)
and then:
val namesList = mutableListOf<PersonName>()
However, in most cases you don't have to explicitly write the types anyway because they can be inferred.
// Is a List<String> and would automatically update if name type changed
val nameList = personList.map(Person::name)
// Or to get an empty mutable list:
val nameList = emptyList<Person>().map(Person::name).toMutableList()
The standard thing to do is to use map to extract the type you need:
val people = listOf(
Person("a", 1),
Person("b", 2),
Person("c", 3),
)
val names = people.map { it.name } // statically inferred to List<String>
If you changed the type of name to something else, you wouldn't need to change the val names = people.map { it.name } line - the new type will be inferred automatically.

Retrieve data class members

I need to check if any variables inside of my data class are null. To do this I need retrieve them first but I can't access them directly (e.g. myDataClass.name) because I need it to be generic. Is there a way to access these variables without directly naming them. For example, like accessing a member of an array (myArray[0]).
The mechanism you're looking for is called "reflection" and it allows to introspect objects at runtime. You'll find a lot of information on the internet, but just to give you a link you may want to check this answer.
In your case you could do something like this:
data class MyDataClass(
val first: String?,
val second: String?,
val third: Int?
)
fun main() {
val a = MyDataClass("firstValue", "secondValue", 1)
val b = MyDataClass("firstValue", null, null)
printProperties(a)
printProperties(b)
}
fun printProperties(target: MyDataClass) {
val properties = target::class.memberProperties
for (property in properties) {
val value = property.getter.call(target)
val propertyName = property.name
println("$propertyName=$value")
}
}
Note that for this code to work you must add kotlin-reflect package as a dependency.