One common uses for sealed classes, in Kotlin, is to have a combination of cases that have some data (using data class) and others that are singletons (using object) like this:
sealed class Location {
object Unknown : Location()
data class Known(val lat: Float, val lon: Float) : Location()
}
The system we are using requires the model to implement Serializable. To my surprise, this doesn't work with object singletons as you can see on this demo:
https://pl.kotl.in/vd_TgUR6a
Which outputs the following:
Success: Known(lat=37.563934, lon=-116.85123) and Known(lat=37.563934, lon=-116.85123) are equal
Failure: sample.Location$Unknown#7c3df479 and sample.Location$Unknown#452b3a41 are not equal
The instance id is different. My guess is that the JVM deserializes it using "artifitial" means. Be it Reflexion or another synthetic way.
How can I make this work?
I found out a "bester" solution. Using the hidden JVM API of readResolve():
sealed class Location: Serializable {
object Unknown : Location() {
private fun readResolve() : Any? = Location.Unknown
}
data class Known(val lat: Float, val lon: Float) : Location()
}
Code here: https://pl.kotl.in/tMGf-AIfq
Produces the following output:
Success: Known(lat=37.563934, lon=-116.85123) and Known(lat=37.563934, lon=-116.85123) are equal
Success: sample.Location$Unknown#452b3a41 and sample.Location$Unknown#452b3a41 are equal
This function is called after an object is loaded from a stream and allows to return a different object instead of the one loaded from memory.
This means that it's safe to use even if our object has a state (not that it should though. And should be much more memory efficient.
There is a ticket about this here: https://youtrack.jetbrains.com/issue/KT-9499
And it looks like it may be coming as part of this other ticket to add the #JVMSerializable annotation: https://youtrack.jetbrains.com/issue/KT-14528
Old Answer:
The best solution I found was to make the singleton object override the default equals, hashCode and toString to make it functionally speaking identical:
sealed class Location: Serializable {
object Unknown : Location() {
override fun equals(other: Any?) = other is Unknown
override fun hashCode() = toString().hashCode()
override fun toString(): String = "Location.Unknown"
}
data class Known(val lat: Float, val lon: Float) : Location()
}
Here is the demo: https://pl.kotl.in/oNd-mnWlQ
And the output is:
Success: Known(lat=37.563934, lon=-116.85123) and Known(lat=37.563934, lon=-116.85123) are equal
Success: Location.Unknown and Location.Unknown are equal
This is a possible solution if memory is not an extreme concern as it will create an object for each of the deserialized objects. Albeit, the object has a very small footprint, which shouldn't be a concern for most cases but something to be aware of.
This wouldn't have been a problem if Kotlin could have implemented object using enum classes as they are serialized differently in the JVM. However, they can't extend classes (only interfaces) so it wouldn't work for this instance.
All that said Oracle plans to drop it eventually:
https://www.infoworld.com/article/3275924/oracle-plans-to-dump-risky-java-serialization.html
In the meantime, we are stuck with this.
Note: This works as long as the object is not mutable. If we allow changing states all hell breaks open as we start having different states on each deserialised object.
Related
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)
}
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 {}
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.
I want to serialize FAIL object via Jackson:
interface OptionalResult<out ResultType : Any> {
val data: ResultType?
object FAIL : OptionalResult<Nothing> {
override val data: Nothing? = null
}
}
What I get is {} but I expect to receive {"data": null}.
How can I fix my object?
By the way, the following object is serialized properly:
object FAIL : OptionalResult<Int> {
override val data: Int? = null
}
Technical problem is that Jackson determines that indicator that would normally indicate existence of a property (public or annotated setter) will be filtered out, as getter is seen as public void getData() that returns nothing.
Filtering is done at low level processing, along with removal of static methods, methods that are neither annotated nor follow naming convention and so on.
It might be possible to improve upon this detection since there is actual difference between void and Void (similar to primitive/Wrapper difference).
But this is the first time such usage has been reported.
One thing that you could try which may (or might not) help: add #JsonProperty for val data. It could help if filtering is only done for non-annotated accessors.
I'm playing with reflection and I came out with this problem. When using bound class reference via the ::class syntax, I get a covariant KClass type:
fun <T> foo(entry: T) {
with(entry::class) {
this // is instance of KClass<out T>
}
}
As I could learn from the docs, this will return the exact type of the object, in case it is instance of a subtype of T, hence the variance modifier.
However this prevents retrieving properties declared in the T class and getting their value (which is what I'm trying to do)
fun <T> foo(entry: T) {
with(entry::class) {
for (prop in memberProperties) {
val v = prop.get(entry) //compile error: I can't consume T
}
}
}
I found that a solution is using javaClass.kotlin extension function on the object reference, to get instead the invariant type:
fun <T> foo(entry: T) {
with(entry.javaClass.kotlin) {
this // is instance of KClass<T>
}
}
This way, I get both the exact type at runtime and the possibility to consume the type.
Interestingly, if I use a supertype instead of a generic, with the latter method I still get access to the correct type, without the need of variance:
class Derived: Base()
fun foo(entry: Base) {
with(entry.javaClass.kotlin) {
println(this == Derived::class)
}
}
fun main(args: Array<String>) {
val derived = Derived()
foo(derived) // prints 'true'
}
If I got it correct, ::class is equal to calling the java getClass, which returns a variant type with a wildcard, while javaClass is a getClass with a cast to the specific type.
Still, I don't get why would I ever need a covariant KClass, when it limits me to only produce the type, given that there are other ways to access the exact class at runtime and use it freely, and I wonder if the more immediate ::class should return an invariant type by design.
The reason for covariance in bound ::class references is, the actual runtime type of an object the expression is evaluated to might differ from the declared or inferred type of the expression.
Example:
open class Base
class Derived : Base()
fun someBase(): Base = Derived()
val kClass = someBase()::class
The expression someBase() is typed as Base, but at runtime it's a Derived object that it gets evaluated to.
Typing someBase()::class as invariant KClass<Base> is simply incorrect, in fact, the actuall result of evaluating this expression is KClass<Derived>.
To solve this possible inconsistency (that would lead to broken type-safety), all bound class references are covariant: someBase()::class is KClass<out Base>, meaning that at runtime someBase() might be a subtype of Base, and therefore this might be a class token of a subtype of Base.
This is, of course, not the case with unbound class references: when you take Base::class, you know for sure that it's the class token of Base and not of some of its subtypes, so it's invariant KClass<Base>.