access property of a data class dynamically like Obj[propertyName] - kotlin

I am learning Kotlin (coming from JS/TS background) and wondering if it is possible to access properties of a data class dynamically. For e.g.
data class Obj (private val name: String){}
val obj = Obj(name="sel")
would it be possible to do now:
val getValueOfField = "name"
println(obj[getValueOfField])
When I tried, it failed but also noticed type inference is an issue. When I did:
Obj::class.declaredMemberProperties.contains(getValueOfField)
to check if the field exists, it still failed to compile due to data inference issue.

No dynamic access is available without using reflection as the return type of such an operation would be unknown and so type safety would be lost.
You can do destructuring though which is almost as flexible:
data class Name (
val firstName: String,
val secondName: String
){}
fun main() {
val (name, _) = Name("Nie", "Selam")
println("$name is learning Kotlin") // prints Nie is learning Kotlin
}
See Kotlin playground.

Related

How can I circumvent Kotlin's generics type variance constraints

I am relatively new Kotlin and Generics kind of give me a headache. I have the following architecture made out of:
A few data classes
A generic interface to process data
Implementations of that processing interface for each data type
A generic processing job class containing the data to be processed and it's appropriate processor
A global (singleton) processor which implements the processing interface, takes processing jobs and just delegates the processing to the job processor. It doesn't care about the data itself at all.
The simplified code looks like this
class DataOne
class DataTwo
interface DataProcessor<in T> {
fun process(o: T)
}
class DataOneProcessor: DataProcessor<DataOne> {
override fun process(o: DataOne) = println("Processing DataOne")
}
class DataTwoProcessor: DataProcessor<DataTwo> {
override fun process(o: DataTwo) = println("Processing DataTwo")
}
class ProcessingJob<T>(val data: T, val processor: DataProcessor<T>)
object GlobalProcessor: DataProcessor<ProcessingJob<Any>> {
override fun process(job: ProcessingJob<Any>) = job.processor.process(job.data)
}
fun main() {
GlobalProcessor.process(ProcessingJob(DataOne(), DataOneProcessor()))
}
In the main function I get a compiler error
Type mismatch.
Required: ProcessingJob<Any>
Found: ProcessingJob<DataOne>
I understand why this happens: A DataProcessor of DataOne, viewed as a DataProcessor of Any could be asked to process DataTwos and for type safety this is not allowed.
Can you give me any suggestions on how/what to change to make it compile and achieve the required result? Thanks for your time!
There are two problems here.
First, Any isn't actually the top-level type. Any implies not null, but T is unconstrained, which means it can be a nullable type. In this case you can use *, or you could also specify the type as Any?.
Change the signature of the GlobalProcessor to this:
object GlobalProcessor: DataProcessor<ProcessingJob<*>> {
override fun process(job: ProcessingJob<*>): ...
The second problem is that the implementation of process can't take advantage of the generic information from the job in order to know that the job.processor and the job.data are compatible. It just sees two objects of unknown type. To let it know they share a compatible type, you need to capture that type as a type variable. We can't add a generic type parameter to the existing method, because it has to match the signature of the interface method, but we can add a new private method that introduces the generic parameter.
Here's the GlobalProcessor with both the required changes.
object GlobalProcessor: DataProcessor<ProcessingJob<*>> {
override fun process(job: ProcessingJob<*>) = processGeneric(job)
private fun <T> processGeneric(job: ProcessingJob<T>) = job.processor.process(job.data)
}

Best Kotlin builder implementation ever?

I work with a lot's of generated kotlin data classes (openapi-generated) with val's and two common fields available and processed. So i can assign values only on construction, like:
data class StringRepresentation {
val value: String,
val available: Boolean,
val processed: Boolean,
}
data class DoubleRepresentation {
val value: Double,
val available: Boolean,
val processed: Boolean,
}
And i have to init lot's of them with common robust code, like:
val currentRepresentation = StringRepresentation("Something", true, false)
Can any pattern or Kotlin language support be used to remove robost object initialization?
It could be wonderful to use some kind of generic template method, something like this:
private inline fun <reified T: StringRepresentation> buildRepresentation(
value: Any,
available: Boolean,
processed: Boolean
): T {
return when(T) {
is StringRepresentation -> StringRepresentation(value.toString(), available, processed)
else -> ...
}
}
, but my types and properties are final and also syntax doesn't allow to set multiple generic boundaries. I can't figure it out the right approach for this. I guess in that case I need to write a builder, but this seems to be java way.
Is there any kotlin way to do this?
I do not think that it is possible with your setup to write a builder that would actually be useful. Your *Representation data types stand in no explicit type relation to their respective type parameter (e.g. a StringRepresentation is not Something<String>), so what should be the return type of a generic builder function? It could only be Any and you would need to cast every result to its expected type in order to use it.
What you can do is to define a generic data class:
data class Representation<T>(
val value: T,
val available: Boolean,
val processed: Boolean,
)
I know, you cannot use that class as super class of your specific data classes, but you can write extension functions that convert the generic representation for one value type into its corresponding specific representation:
fun Representation<String>.typed() = StringRepresentation(value, available, processed)
fun Representation<Double>.typed() = DoubleRepresentation(value, available, processed)
Then you can use the same code to create a data object of any type:
val stringRepresentation: StringRepresentation = Representation("x", false, true).typed()
val doubleRepresentation: DoubleRepresentation = Representation(1.0, false, true).typed()
But please note that this is still not a generic solution since whatever you put into the constructor Representation has to be typed explicitly as a String or Double, respectively.
Let's say you define a generic function for all undefined value types:
fun <T: Any> Representation<T>.typed(): Any = error("no typed representation for value type ${value::class}")
The specific cases above will still work, and you could additionally write something like this:
val x : Any = 2.0
val someRep: Any = Representation(x, true, false).typed()
This is syntactically correct and will compile, but it will not work as desired, because what you get is an IllegalArgumentException ("no typed representation for value type class kotlin.Double"), because x is not typed as Double, but as Any.

Kotlin Flow out generic

I am using flow{} builder to call the api and then emit() the response to ViewModel. I add return type of flow as Flow<Resource<List<RemoteData>>>. However, at some places in emit(), the Android Studio throws
error : Not enough information to infer type variable T
Because the emit(Resource.Error(message = "Couldn't reach server, check your internet connection.")) is expecting values of type List<RemoteData> Please see my Resource class below
sealed class Resource<T>(val data: T? = null, val message: String? = null) {
class Loading<T>(data: T? = null): Resource<T>(data)
class Success<T>(data: T?): Resource<T>(data)
class Error<T>(message: String, data: T? = null): Resource<T>(data, message)
}
My question, Is it safe to change emit to
emit(Resource.Error(
message = "Couldn't reach server, check your internet connection.",
data = null
))
And flow's return type as Flow<Resource<out List<RemoteData>>> ?
Kotlin has declaration site variance. I would put out at the Resource class declaration. Then when you declare your type Flow<Resource<List<RemoteData>>>, it will already be implicitly out List<RemoteData>.
Also, your Resource classes look convoluted to me. If data is the loaded resource, it should not be part of the Loading or Error classes. Why force every instance of Loading and Error to carry a meaningless null data value? Likewise, the message should not be part of the Loading and Success cases.
I would rewrite your sealed class as a sealed interface (since it has no shared state between types) like this, and take advantage of data class features as well. Loading can be an object because it doesn't need to hold state. Loading and Error can both be Resource<Nothing> since the type T is irrelevant to any specific instance of them. That way you won't have to needlessly specify types when using them, like having to put <RemoteData> after is Resource or is Error in a when statement.
sealed interface Resource<out T> {
object Loading: Resource<Nothing>
data class Success<out T>(val data: T): Resource<T>
data class Error(val message: String): Resource<Nothing>
}
This version of the sealed classes will be much easier to use. The compiler will be more lenient with how and where you need to specify generic types.

Filter out null in immutable objects list of fields

I have an immutable object:
class Foo(
val name: String,
val things: List<Thing>
)
A third party lib creates the Foo object with some 'null' Thing objects.
I am creating a new object:
val foo = thirdPartyGetFoo()
val filteredFoo = Foo(foo.name, foo.things.filterNotNull())
That works, however AndroidStudio greys out the filterNotNull function call and presents a warning:
Useless call on collection type: The inspection reports filter-like
calls on already filtered collections.
Is this the right way to filter that list? Should I ignore the warning or is there a better way?
You do not specify what library creates the object with nulls. Some deserialization libraries can use static factory methods which you could configure, and then have the factory method strip the null. For example, if this were Jackson you would simply:
class Foo(val name: String, val things: List<Thing>) {
companion object {
#JsonCreator
#JvmName("createFromNullable")
fun create(name: String, things: List<Thing?>) = Foo(name, things.filterNotNull())
fun create(name: String, things: List<Thing>) = Foo(name, things)
}
}
Then...
val goodFoo = jacksonObjectMapper().readValue<Foo>(someJsonWithNulls)
Maybe your library has options that are similar?
If not, and you don't have 100 of these things with this problem, I would probably create a temporary class to hold the results and convert that to the final class:
open class FooNullable(val name: String, open val things: List<Thing?>) {
open fun withoutNulls(): Foo = Foo(name, things.filterNotNull())
}
class Foo(name: String, override val things: List<Thing>) : FooNullable(name, things) {
override fun withoutNulls(): Foo = this
}
Then you can deserialize into FooNullable and just call withoutNulls() to get the other flavor that is clean. And if you accidentally call it on one without nulls already, it just does nothing.
val goodFoo = Foo("", emptyList<Thing>())
val alsoGoodFoo = goodFoo.withoutNulls() // NOOP does nothing
val badFoo = thirdPartyGetFoo()
val betterFoo = badFoo.withoutNulls() // clean up the instance
val safeFoo = thirdPartyGetFoo().withoutNulls() // all at once!
Not the cleanest, but does work. The downsides is this second step, although it looks like you were already planning on doing that anyway. But this model is safer than what you proposed since you KNOW which type of object you have and therefore you continue to be typesafe and have the compiler helping you avoid a mistake.
You don't have to use inheritance as in the above example, I was just trying to unify the API in case there was a reason to have either version in hand and know which is which, and also act upon them in a similar way.

How do I get the class name from a type name?

I am trying to deserialize a Json string into an object of type OperationResult<String> using Jackson with Kotlin.
I need to construct a type object like so:
val mapper : ObjectMapper = ObjectMapper();
val type : JavaType = mapper.getTypeFactory()
.constructParametricType(*/ class of OperationResult */,,
/* class of String */);
val result : OperationResult<String> = mapper.readValue(
responseString, type);
I've tried the following but they do not work.
val type : JavaType = mapper.getTypeFactory()
.constructParametricType(
javaClass<OperationResult>,
javaClass<String>); // Unresolved javaClass<T>
val type : JavaType = mapper.getTypeFactory()
.constructParametricType(
OperationResult::class,
String::class);
How do I get a java class from the type names?
You need to obtain instance of Class not KClass. To get it you simply use ::class.java instead of ::class.
val type : JavaType = mapper.typeFactory.constructParametricType(OperationResult::class.java, String::class.java)
Kotlin has a few things that become a concern when using Jackson, GSON or other libraries that instantiate Kotlin objects. One, is how do you get the Class, TypeToken, TypeReference or other specialized class that some libraries want to know about. The other is how can they construct classes that do not always have default constructors, or are immutable.
For Jackson, a module was built specifically to cover these cases. It is mentioned in #miensol's answer. He shows an example similar to:
import com.fasterxml.jackson.module.kotlin.* // added for clarity
val operationalResult: OperationalResult<Long> = mapper.readValue(""{"result":"5"}""")
This is actually calling an inline extension function added to ObjectMapper by the Kotlin module, and it uses the inferred type of the result grabbing the reified generics (available to inline functions) to do whatever is needed to tell Jackson about the data type. It creates a Jackson TypeReference behind the scenes for you and passes it along to Jackson. This is the source of the function:
inline fun <reified T: Any> ObjectMapper.readValue(content: String): T = readValue(content, object: TypeReference<T>() {})
You can easily code the same, but the module has a larger number of these helpers to do this work for you. In addition it handles being able to call non-default constructors and static factory methods for you as well. And in Jackson 2.8.+ it also can deal more intelligently with nullability and default method parameters (allowing the values to be missing in the JSON and therefore using the default value). Without the module, you will soon find new errors.
As for your use of mapper.typeFactory.constructParametricType you should use TypeReference instead, it is much easier and follows the same pattern as above.
val myTypeRef = object: TypeReference<SomeOtherClass>() {}
This code creates an anonymous instance of a class (via an object expression) that has a super type of TypeRefrence with your generic class specified. Java reflection can then query this information.
Be careful using Class directly because it erases generic type information, so using SomeOtherClass::class or SomeOtherClass::class.java all lose the generics and should be avoided for things that require knowledge of them.
So even if you can get away with some things without using the Jackson-Kotlin module, you'll soon run into a lot of pain later. Instead of having to mangle your Kotlin this module removes these types of errors and lets you do things more in the "Kotlin way."
The following works as expected:
val type = mapper.typeFactory.constructParametricType(OperationalResult::class.java, String::class.java)
val operationalResult = mapper.readValue<OperationalResult<String>>("""{"result":"stack"}""", type)
println(operationalResult.result) // -> stack
A simpler alternative to deserialize generic types using com.fasterxml.jackson.core.type.TypeReference:
val operationalResult = mapper.readValue<OperationalResult<Double>>("""{"result":"5.5"}""",
object : TypeReference<OperationalResult<Double>>() {})
println(operationalResult.result) // -> 5.5
And with the aid of jackson-kotlin-module you can even write:
val operationalResult = mapper.readValue<OperationalResult<Long>>("""{"result":"5"}""")
println(operationalResult.result)