Kotlin Flow out generic - kotlin

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.

Related

Is there a way to use, "Extends/ Implements any Type" (say, any Serializable) as Type for Kotlin properties

In my Jetpack compose project, I have a field that can contain different types of serializable data.
val httpBody : Serializable?
get() = when(this) {
is InitiatePayment -> this.pendingTransaction //data class annotated with #Serializable
is VerifyPaymentConfirmation -> this.transactionId //Normal String
is ValidateCart -> this.cartItems //Another data class with #Serializable
else -> null
}
Any? Type works. But when using Serializable?,
Gives the error,
Type mismatch.
Required:
Serializable?
Found:
PendingTransactionDTO
In Swift, I could just do
var httpBody : Codable? {
switch self {
case .initiatePayment(let pendingTransaction):
return pendingTransaction //Codable struct
case .verifyStoreCode(let storeCode):
return storeCode //String
case .verifyPaymentConfirmation(let transactionId):
return transactionId //String
default:
return nil
}
}
In this case, httpBody property accepts everything that implements the Codable interface/protocol.
In short, how to get the property type to accept any class/data class/object that is annotated with #Serializable, plus primitives like String and Int, which I suppose are by default, serializable in Kotlin.
Any tips? Or is it even possible to do this in Kotlin?
Are you using the #Serializable annotation from the kotlinx.serialization package? That's just to mark the class for processing by that library - it's unrelated to the Serializable interface in Java which is the type you're using here. That annotation isn't part of the type system, so you can't use it for this kind of thing.
Depending on how you're actually doing the serialisation, it might be enough for you to add the Serializable interface type to your class definitions - like the docs for the interface say, it doesn't actually require any methods to be overridden. But if you want to, say, throw it into a Bundle with putSerializable, you'll probably have more work to do.
Serializable is an annotation, not a type that a class inherits (unless you're talking about Java's unrelated Serializable interface), so this is not possible. Notably, the various Kotlinx Serialization libraries' encodeTo... functions have no restriction on the type of the argument. There is no compile-time check to make sure it is Serializable. You might as well make your function return Any?. The Any? return type is an acceptable argument for encodeToString, et. al.

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

Kotlin compile error using self generic type bound

Suppose I have the following code to simulate a state machine in Kotlin:
sealed interface State {
object A : State
object B: State
object C: State
object D: State
}
interface StateMachine<Self: StateMachine<Self, *>, T: State>
fun <S : StateMachine<S, State.A>> S.transitionX() = object : StateMachine<S, State.B> {}
fun <S: StateMachine<S, State.B>> S.transitionQ() = object : StateMachine<S, State.B> {}
object Start: StateMachine<Start, State.A>
fun main() {
val stateMachine = Start.transitionX().transitionQ()
}
However, this doesn't compile because
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun <S : StateMachine<TypeVariable(S), State.B>> TypeVariable(S).transitionQ(): StateMachine<TypeVariable(S), State.B> defined in root package in file Main.kt
which is probably because of the Self generic constraint.
Ideally, stateMachine should have a type StateMachine<StateMachine<Start, State.A>, State.B.
I was wondering if there's any way to fix the generic constraints so that this does compile? Note: I am aware that the Self generic parameter isn't actually needed for this state machine, but I'm just interested to see if this is actually possible.
I have tried a few different changes to the generic type bounds, but the closest I could get resulted in stateMachine just having a type of StateMachine<Start, State.B>, which isn't quite what I want. Other changes I've made have just caused the Kotlin Finite Bound Restriction error.
Any help is appreciated, thanks!
I don't know what you're trying to do with the self type, so it's hard to say whether these solutions will actually work for your use case.
You don't need to involve new generics in your function itself, only within its receiver and return type. So you can use * types to represent Self. This of course assumes that the Self type isn't needed outside its own private implementation, like if you had a fun copy(): Self. It's impossible to define an implementation of your interface using an anonymous object, since it has to have a class name to be able to describe its own self type. So you either need to define it with a named object outside the function, or by defining a class inside the function and returning an instance of it.
fun StateMachine<*, State.A>.transitionX(): StateMachine<*, State.B> {
class Impl: StateMachine<Impl, State.B>{
}
return Impl()
}
You could define explicit interfaces for all the possible children and use those. Since State is sealed, this is possible.
interface AStateMachine: StateMachine<AStateMachine, State.A>
interface BStateMachine: StateMachine<BStateMachine, State.B>
interface CStateMachine: StateMachine<CStateMachine, State.C>
interface DStateMachine: StateMachine<DStateMachine, State.D>
fun AStateMachine.transitionX() = object : BStateMachine {}
fun BStateMachine.transitionQ() = object : CStateMachine {}

Subtypes not being recognized in Subclasses

I have the following code setup;
abstract class GenericQuestionEditor() {
protected abstract var data: GenericQuestionData
}
but then when I create EditorSimple() it throws an error when I try to set data to DataSimple(), why?
class EditorSimple(): GenericQuestionEditor() {
override var data = DataSimple()
}
my GenericQeustionData and DataSimple() are setup like this;
abstract class GenericQuestionData {}
class DataSimple: GenericQuestionData() {}
it doesn't complain if I create this function in GenericQuestionEditor()
fun test() {
data = DataSimple()
}
Why do I get an error on data in EditorSimple()? It should recognize it as a subtype and it should be allowed as I understand.
I feel like the answer is found in the kotlin documentation but i'm not sure how to configure it in this case since they are not passed values or part of a collection.
You need to specify the type explicitly:
class EditorSimple(): GenericQuestionEditor() {
override var data: GenericQuestionData = DataSimple()
}
Without the type annotation, the type of data would be inferred to be DataSimple, which doesn't match the type of its super class' data. Even though the types are related, you can't override writable a property with a subtype. Imagine if I did:
class SomeOtherData: GenericQuestionData()
val editor: GenericQuestionEditor = EditorSimple()
editor.data = SomeOtherData() // data is of type GenericQuestionData, so I should be able to do this
But, editor actually has a EditorSimple, which can only store DataSimple objects in data!

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)