Is it possible to implement both List<SuperClass> and List<SubClass>? - kotlin

Let's take the following code as an example:
val immutableList: List<Any> = listOf<String>()
val mutableList: MutableList<Any> = mutableListOf<String>()
interface SuperList : List<Any>
interface SubList : SuperList, List<String>
As expected, assigning immutableList is allowed, which from my understanding of the docs is because it's marked to say it will only ever return values of T and never take them, so it doesn't matter if it's Any or a subclass.
Also as expected, assigning mutableList gives an error because it cannot offer that guarantee, as casting to MutableList<Any> would let you add an Any to a list of Strings and that would be bad.
I would expect the interface SubList to be fine for the same reason that immutableList is: List's generic functions will only give T, never take it, so returning a String would make both happy. However, its declaration throws the same error as mutableList:
Type parameter E of 'List' has inconsistent values: String, Any
Type parameter E of 'Collection' has inconsistent values: String, Any
Type parameter T of 'Iterable' has inconsistent values: String, Any
Why is this?
Things I've attempted, when trying to understand the cause:
Having SubList inherit from List<Any> directly rather than SuperList: Gives the same error, so this isn't due to something funky with the layers in the inheritance.
Having SuperList inherit from List<out Any> rather than List<Any>: Gives the error Projections are not allowed for immediate arguments of a supertype.
Having SuperList take a type parameter. This works but like... at that point why does SuperList even exist, lol. Much better for my use case to just take an entirely different approach to the goal than to do that.
Context:
My goal was a pair of Table and MutableTable types, and my initial idea was implementing this via extending List<List> and List<MutableList>, respectively. But I wanted to boil the question down to its simplest form, and so chose non-generic classes to use for the sample code.
I have other ideas on how to implement the types, so I'm not looking for an answer to that. I'd just like to understand the root issue that stops this particular approach from working, so that in the future I don't run into other pitfalls with it in ways that might be harder to dodge.

Related

Map of generic interfaces in Kotlin

I stuck with some simple thing) Let's say I have following:
interface IMessagePayload // marker interface
data class IdPayload(
val id: Long
) : IMessagePayload
data class StringPayload(
val id: String,
) : IMessagePayload
Then I have a class:
data class Message<T : IMessagePayload>(
val id: String,
val payload: T,
)
Also I have some interface describing processor of this message:
interface IMessageProcessor<T : IMessagePayload> {
fun process(message: Message<T>)
}
And some implementation:
class ProcessorImpl : IMessageProcessor<IdPayload> {
override fun process(message: Message<IdPayload>) {
}
}
Now I wanna have a map of such processors. Lets use some enum type as a keys of this map:
enum class ActionType {
UPDATE,
DELETE,
ADD
}
private var map = mutableMapOf<ActionType, IMessageProcessor<IMessagePayload>>()
map[ActionType.ADD] = ProcessorImpl() // <-- error here
And that's where the problem occurs. I cannot put my ProcessorImpl into this map. The compiler says that there is an error: Type mismatch. Required: IMessageProcessor. Found: ProcessorImpl().
I could declare the map in the following way (using star projection):
private var map = mutableMapOf<ActionType, IMessageProcessor<*>>()
But in this case I cannot call processors's process method fetching it from the map by key first:
map[ActionType.ADD]?.process(Message("message-id", IdPayload(1))) // <-- error here
Compiler complains: Type mismatch. Required nothing. Found Message<IdPayload>
What am I doing wrong? Any help is appreciated.
This is about variance.
IMessageProcessor is defined as interface IMessageProcessor<T : IMessagePayload>; it has one type parameter, which must be IMessagePayload or a subtype.
But it is invariant in that type parameter; an IMessageProcessor< IdPayload> is not related to an IMessageProcessor<IMessagePayload>.  In particular, it's not a subtype.
And your map is defined with a value type IMessageProcessor<IMessagePayload>.  So its value cannot be an IMessageProcessor< IdPayload>, because that's neither the value type, nor a subtype.  Hence the compile error.
In this case, the simplest way to get it to compile is to change your map:
private var map = mutableMapOf<ActionType, IMessageProcessor<out IMessagePayload>>()
The only difference there is the out; that tells the compiler that the value IMessageProcessor is covariant in its type parameter.  (It may help to think of out as meaning ‘…or any subtype’.  Similarly, you could make it contravariant by using in, which you might think of as ‘…or any supertype’.)
This lets you store in the map an IMessageProcessor for any subtype of IMessagePayload.
However, if you do that, you'll find that you can't use any value you pull out of your map — because it can't tell which messages the processor can handle, i.e. which subtype of IMessagePayload it works for!  (The compiler expresses this as expecting a type parameter of Nothing.)
In general, it's often better to specify variance on the interface or superclass itself (declaration-site variance) rather than the use-site variance shown above.  But I can't see a good way to do that here, because you have multiple generic classes, and they interact in a complicated way…)
Think for a moment what IMessageProcessor's type parameter means: it's the type of message that the processor can consume. So an IMessageProcessor<A> can handle messages of type Message<A>.
Now, a subtype must be able to do everything its supertype can do (and usually more) — otherwise you can't drop that subtype anywhere that's expecting to use the supertype.  (That has the grand name of the Liskov substitution principle — but it's really just common sense.)
So an IMessageProcessor<B> is a subtype of IMessageProcessor<A> only if it can handle at least all the messages that an IMessageProcessor<A> can.  This means it must accept all messages of type Message<A>.
But Message is invariant in its type parameter: a Message<B> is not directly related to a Message<A>.  So you can't write a processor that handles them both.
The most natural solution I can find is to specify variance on both Message and IMessageProcessor:
data class Message<out T : IMessagePayload>( /*…*/ )
interface IMessageProcessor<in T : IMessagePayload> { /*…*/ }
And then use a wildcard in your map to make it explicit that you don't know anything about the type parameters of its values:
private var map = mutableMapOf<ActionType, IMessageProcessor<*>>()
That lets you safely store a ProcessorImpl() in the map.
But you still have to use an (unchecked) cast on the values you pull out of the map before you can use them:
(map[ActionType.ADD] as IMessageProcessor<IdPayload>)
.process(Message("4", IdPayload(4L)))
I don't think there's any easy way around that, because the problem is inherent in having values which are processors that can handle only some (unknown) types of message.
I'm afraid the best thing would be to have a rethink about what these classes mean and how they should interact, and redesign accordingly.

How can I create an instance of KClass<MutableList<Foo<*>>>?

I need to pass the type of a class as a parameter because of type erasure.
class Abc<T : Any>(private val clazz: KClass<T>)
I can get it to work when T is something like String, but I'm having trouble creating the argument for clazz when the type is KClass<MutableList<Foo<*>>>.
I've tried doing mutableListOf<Foo<*>>(), but then I get KClass<MutableList<out Foo<*>>> instead of KClass<MutableList<Foo<*>>>.
How can I create the KClass instance that I need?
If you need to construct an Abc<MutableList<Foo<*>>> so its methods end up taking and returning MutableList<Foo<*>>, it's enough to cast it:
val abc = Abc(MutableList::class) as Abc<MutableList<Foo<*>>>
(you could cast the argument to KClass<MutableList<Foo<*>>> instead, but this makes no difference).
But as Tenfour04's comment says, there are no different KClass instances for MutableList<Foo<*>>, MutableList<String>, etc. so:
you can't expect actually different behavior for Abc<MutableList<Foo<*>>> and Abc<MutableList<AnythingElse>> except for the casts the compiler inserts;
by using type erasure in this way, you are giving up some type safety, and make possible ClassCastExceptions far from the original cast.

Kotlin: Assert Immutability

I have class that internally maintains a mutable list, and I want to provide an immutable view on this list. Currently I'm using the following:
/**The list that actually stores which element is at which position*/
private val list: MutableList<T> = ArrayList()
/**Immutable view of [list] to the outside.*/
val listView: List<T> get() = list.toList()
First question: Can this be done easier
Second question: How can I test that listView is actually immutable. I guess reflections are necessary?
If you only needed the compile-time type to be immutable, you could simply upcast your list:
val listView: List<T> get() = list
(Though if you checked and downcast that to MutableList, you could make changes — and those would affect the original list.)
However, if you want full immutability, that's tricky.  I don't think there are any general-purpose truly immutable lists in the Kotlin stdlib.
Although List and MutableList look like two different types, in Kotlin/JVM they both compile down to the same type in the bytecode, which is mutable.  And even in Kotlin, while Iterable.toList() does return a new list, the current implementation* actually gives a MutableList that's been upcast to List.  (Though mutating it wouldn't change the original list.)
Some third-party libraries provide truly immutable collections, though; see this question.
And to check whether a List is mutable, you could use a simple type check:
if (listView is MutableList)
// …
No need to use reflection explicitly.  (A type check like that is considered to be implicit reflection.)
(* That can change, of course.  It's usually a mistake to read too much into the current code, if it's not backed up by the documentation.)

Kotlin non nullable map allows remove null

Why this code can be compiled and executed without erros?
val map = HashMap<Int, Long>()
val key :Int? = null
map.remove(key)
In MutableMap remove declared as accepting only non nullable key, so it shouldn't even compile. Is it a Kotlin type inference bug or am I missing something?
public fun remove(key: K): V?
Your code is perfectly fine as remove() allows nullable arguments - your map contents definition got nothing to it. When remove() is invoked, it would try to find matching requested key in the map and as it's not there (it's completely irrelevant why it's not there - it's valid case for key to be not present) nothing will happen. Where compiler will complain is on any attempt to put such key into your map. Then map definition kicks in and since it's known that nullable keys not allowed, such code won't even compile as this is clearly buggy code.
In this case, map.remove(key) doesn't not calls
public fun remove(key: K): V?
It calls an extension remove function:
public inline fun <#OnlyInputTypes K, V> MutableMap<out K, V>.remove(key: K): V? =
#Suppress("UNCHECKED_CAST") (this as MutableMap<K, V>).remove(key)
This function documentation says that it allows to overcome type-safety restriction of remove that requires to pass a key of type K.
It allows overcoming type-safety restriction because the key of the entry you are removing does not have to be the same type as the object that you pass into remove(key); the specification of the method only requires that they be equal. This follows from how the equals() method takes in an Any as a parameter, not just the same type as the object.
Although it may be commonly true that many classes have equals() defined so that its objects can only be equal to objects of its own class, there are many places where this is not the case. For example, the specification for List.equals() says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List. So, for example, according to the specification of the method, it is possible to have a MutableMap<ArrayList<Something>, Something> and call remove(key) with a LinkedList as an argument, and it should retrieve the key which is a list with the same contents. This would not be possible if this extension remove(key) didn't exist.[1]
Kotlin could warn or refuse to compile (would be good), but it doesn't (for now).
The reason for it being not as bad as it looks from a first glance is that you cannot put an Int? into a MutableMap<Int, Long> because
val map = HashMap<Int, Long>()
val key :Int? = null
map.put(key, 1) // <--- WON'T COMPILE [Type mismatch: inferred type was Int? but Int was expected]
map.remove(key)
Nevertheless, I think you are right by wondering about that method being compiled.
Eventually asking this question helped to find another question with explanation. In short, what actually happens is call of the extension function which have it's own type inference.

Kotlin - "in" keyword - what is is used for?

i am trying to understand when to use the "in" keyword in generics as opposed to the "out" keyword (which allows assigning to subclasses).
I am actually following this tutorial if it matters.
Lets say we have the following class defintiion:
class ParameterizedConsumer<in T> {
fun toString(value: T): String {
return value.toString()
}
}
How does this even compile since value is not guaranteed to be a String ? is this what the in keyword does ? it tells the class that there is a guarantee the type wont be any other subclass ? I am just not clear on the usecase for it, can you help ?
the tutorial says i will be able to call the following but i am lost as to what it has changed:
val parameterizedConsumer = ParameterizedConsumer<Number>()
val ref: ParameterizedConsumer<Double> = parameterizedConsumer
assertTrue(ref is ParameterizedConsumer<Double>)
UPDATE: I get it now. Out means you can downcast when producing. and "In" means you can downcast when assigning.
So in java this is not allowed:
// Java
void demo(Source<String> strs) {
Source<Object> objects = strs; // !!! Not allowed in Java
// ...
}
but in kotlin we can fix that if we use the "out" keyword we can assign to a downcasted class (subclass). likewise with "in" we can pass in a subclass into the class internally to use but not outwardly.
it tells the class that there is a guarantee the type wont be any other subclass ? I am just not clear on the usecase for it, can you help ?
Say you have a function that wants to add some items to a list you supply. The items are of type Int.
Question: what kinds of list are acceptable to this function?
Answer: MutableList<Int>, MutableList<Number>, MutableList<Any>. Or, in short, MutableList<in Int>.
In the same spirit, let's explain the out projection.
Say you have a function that wants to get some elements from a list you supply. The items are of type Future.
Question: what kinds of list are acceptable to this function?
Answer: List<Future>, List<RunnableFuture>, List<ScheduledFuture>... or, in short, List<out Future>.
I'll answer part of your question
How does this even compile since value is not guaranteed to be a String
So what? You can call .toString() on any type. That's how you get a string you'll be returning.