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

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.

Related

Kotlin. How to get specific subclass of sealed class?

I'm using kotlin sealed class. And I need to retrieve specific subclass. My sealed class:
sealed class Course(
val type: Type
) {
data class ProgrammingCourse(val name: String, val detail: String) : Course(Type.PROGRAMMING)
object LanguageCourse: Course(Type.LANGUAGE)
.....
}
For example I have function which can return Course:
fun getCourse(): Course {
if(...)
return Course.ProgrammingCourse("test", "test")
else
return Course.LanguageCourse
}
In addition, I have a method that can only work with a specific subclass of the Course class. Fox example:
fun workWithCourse(course: Course.ProgrammingCourse) {
// here some logic
}
And now I'm trying to get the course using the method getCourse(), and then pass it to the method workWithCourse()
fun main() {
val course = getCourse()
workWithCourse(course)
}
Error:
Type mismatch.
Required:
Course.ProgrammingCourse
Found:
Course
But I know the course type - Type, parameter that each course has. Can I, knowing this Type, cast the course (which I retrieve from getCourse() method) to a specific subclass ? Is there such a way ?
Please help me
P.S.
I don't need type checks like:
if(course is Course.ProgrammingCourse) {
workWithCourse(course)
}
I need the subclass to be automatically inferred by the Type parameter, if possible.
P.S.2
The need for such a solution is that I have a class that takes a Course, it doesn't know anything about a particular course, at the same time the class takes the Type that I want to use for identification. This class also receives an interface (by DI) for working with courses, a specific implementation of the interface is provided by the dagger(multibinding) by key, where I have the Type as the key. In the same way I want to pass by the same parameter Type specific subclass of my Course to my interface which working with specific courses.
No, there is no way for automatic inference to the best of my knowledge.
You returned a Course, and that's what you have. Being sealed here does not matter at all. Generally what you do here is use the when expression if you want to statically do different things depending on the type, but if it's just one type (ProgrammingCourse) that can be passed to workWithCourse, then an if is probably right, with dispatch using as.
That said, this looks like counter-productive design. If you can only work with one course, why do they even share a top level interface? The way the code is written implies working is a function that can take any course, or should be a method member. Anything else is very confusing. Perhaps workWithCourse should take a Course and use the when expression to dispatch it appropriately?
In kotlin you can specify the class explicitly with as.
val course = getCourse()
if (type == Type.PROGRAMMING) {
workWithCourse(course as Course.ProgrammingCourse)
}
*thanks Joffrey for his comment
What you seem to be asking for is a compile-time guarantee for something that will only be known at runtime. You didn't share the condition used in getCourse(), but in general it could return both types.
Therefore, you need to decide what will happen in both cases - that's not something the compiler can decide for you via any "inference".
If you want the program to throw an exception when getCourse() returns something else than a Course.ProgrammingCourse, you can cast the returned value using as:
val course = getCourse() as Course.ProgrammingCourse
workWithCourse(course)
If you don't want to crash, but you only want to call workWithCourse in some cases, then you need an if or when statement to express that choice. For instance, to call it only when the value is of type Course.ProgrammingCourse, then you would write the code you already know:
if (course is Course.ProgrammingCourse) {
workWithCourse(course)
}
Or with a when statement:
val course = getCourse()
when (course) {
is Course.ProgrammingCourse -> workWithCourse(course)
is Course.LanguageCourse -> TODO("do something with the other value")
}
The when is better IMO because it forces you (or other devs in the team) to take a look at this when whenever you (or they) add a new subclass of the sealed class. It's easy to forget with an if.
You can also decide to not test the actual type, and focus on the type property like in #grigory-panov's answer, but that is brittle because it relies on an implicit relationship between the type property and the actual type of the value:
val course = getCourse()
if (type == Type.PROGRAMMING) {
workWithCourse(course as Course.ProgrammingCourse)
}
The main point of using sealed classes is so you can use their actual type instead of a manually managed type property + casts. So I'd say use only is X and don't set a type property at all. Using a sealed class allows Kotlin to type-check a bunch of things, it's more powerful than using such a property.

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

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.

Is it possible to pass null type in place of generic type parameter?

I am going to use the following method from Spring Data Kotlin extensions:
inline fun <reified T : Any> MongoOperations.bulkOps(bulkMode: BulkMode, collectionName: String? = null): BulkOperations
The question is: can I somehow avoid specifying T assuming I do not want to provide entity class name (that's because I will explicitly specify collectionName, and in this case class type can be null). I would like to type something like:
val ops = mongoTemplate.bulkOps<null>(BulkOperations.BulkMode.UNORDERED, collectionName = "i_know_better")
Is there a type literal for null with which I can parameterize bulkOps?
I think the short answer is no.
You seem to confuse types with values. null is a value and not a type so it cannot be used as a type in generic methods.
In your specific example, even if you could use null, looking at the code what would you expect to happen?
#Suppress("EXTENSION_SHADOWED_BY_MEMBER")
inline fun <reified T : Any> MongoOperations.bulkOps(bulkMode: BulkMode, collectionName: String? = null): BulkOperations =
if (collectionName != null) bulkOps(bulkMode, T::class.java, collectionName)
else bulkOps(bulkMode, T::class.java)
As you can see there's always T::class.java being called. What would be the result of null::class.java?
I'm unfamiliar with the Spring Data so I can't really provide an alternative, but I'd say you either need to search for another method or use an appropriate class here. The generic type is marked as T : Any so presumably it can be any non-nullable type. I wonder if Unit would work. Again, I'm not sure what this class is used for.
To answer the question in general, you can use Nothing? to represent the type that only contains the value null.
That being said, as #Fred already said, the method you're considering here explicitly states T : Any, meaning only non-nullable types are allowed. And it makes sense given that the function is accessing the class of T.

Type parameter cannot have any other bounds if it's bounded by another type parameter: what does this mean and how to resolve it?

I was implementing custom list class MyList<T> in kotlin. In that, I wanted to add insertSorted function, which inserts a new element into the list in sorted order. For that, T must implement comparator. So the prototype of that function will be fun <C> insertSorted(ele: C) where C:T, C:Comparable<T> But this is giving me Type parameter cannot have any other bounds if it's bounded by another type parameter error. I am not understanding what this error is. Also, this question did not help me much.
PS: The type I am passing to that function is declared as class MyClass : Comparator<MyClass>. So the bound where C:T, C:Comparator<T> is valid I guess.
For the meaning of the error, see this question:
Why can't type parameter in Kotlin have any other bounds if it's bounded by another type parameter?
But if your custom list contains elements of type T and you want to compare them, then T should implement Comparable<T>.
So this should be all you need:
class MyList<T: Comparable<T>> {
fun insertSorted(ele: T) {
}
}

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.