Kotlin Wildcard Capture on List Callback Parameter - kotlin

Java:
public class JavaClass implements ModelController.Callback {
#Override
public void onModelsLoaded(#NonNull List<? extends Model> models) {
doSomething(models);
}
private void doSomething(List<Model> models) { }
}
Kotlin:
class ModelController {
var callback = WeakReference<Callback>(null)
interface Callback {
fun onModelsLoaded(models: List<Model>)
}
fun someFunction() {
callback.get().onModelsLoaded(ArrayList<Model>())
}
}
interface Model {
}
Without the ? extends Model in the Java onModelsLoaded method, the override doesn’t match the interface made in Kotlin. With it, I get the following error:
doSomething(<java.util.List<com.yada.Model>) cannot be applied to (java.util.List<capture<? extends com.yada.Model>>)
Why is the wildcard capture required and why doesn't it allow it to be used against the non-wildcard method?

The issue stems from Kotlin collections being variant, and Java only having use-site variance which is implemented though wildcards (capture is something connected to wildcards but not exactly the ? extends ... syntax itself).
When in Kotlin we say List<Model> it means "read-only list of Model or subtypes of Model", when we say the same in Java it means "mutable list of exactly Model and nothing else". To mean roughly what Kotlin's List<Model> means, in Java we have to say List<? extends Model>, this is why for the override to work you have to add the wildcard into the Java code.
Now, your doSomething is written in Java and says that it wants "a list of exactly Model", and when you are giving it "a list of Model or its subtypes", the Java compiler complains, because it can be dangerous: doSomething might try to do something that is not legitimate for a list of, say, ModelImpl, because it thinks it's working on a list of Model.
As of now (Kotlin Beat 2), you have two options:
use MutableList<Model> in your Kotlin code - this mean exactly what Java's List<Model> means, or
define doSomething so that it takes List<? extends Model>, which is what your current Kotlin code means.
In the next update of Kotlin we'll add an annotation on types to facilitate a somewhat cleaner workaround for this problem.

To solve the problem with capture<? extends Model>
You may do something like this:
void doSomething(List<Model> models) {
new ArrayList(models)
}

Related

Kotlin sealed classes vs using polymorphism

I'm curious about an example given in Kotlin documentation regarding sealed classes:
fun log(e: Error) = when(e) {
is FileReadError -> { println("Error while reading file ${e.file}") }
is DatabaseError -> { println("Error while reading from database ${e.source}") }
is RuntimeError -> { println("Runtime error") }
// the `else` clause is not required because all the cases are covered
}
Let's imagine the classes are defined as follows:
sealed class Error
class FileReadError(val file: String): Error()
class DatabaseError(val source: String): Error()
class RuntimeError : Error()
Is there any benefit for using when over using polymorphism:
sealed class Error {
abstract fun log()
}
class FileReadError(val file: String): Error() {
override fun log() { println("Error while reading file $file") }
}
class DatabaseError(val source: String): Error() {
override fun log() { println("Error while reading from database $source") }
}
class RuntimeError : Error() {
override fun log() { println("Runtime error") }
}
The only reason I can think of is that we may not have access to the source code of those classes, in order to add our log method to them. Otherwise, it seems that polymorphism is a better choice over instance checking (see [1] or [2] for instance.)
This is described as "Data/Object Anti-Symmetry" in the book Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin.
In the first example (Data style), you are keeping your error classes dumb with an external function that handles all types. This style is in opposition to using polymorphism (Object style) but there are some advantages.
Suppose you were to add a new external function, one that returns an icon to show the user when the error happens. The first advantage is you can easily add this icon function without changing any line in any of your error classes and add it in a single place. The second advantage is in the separation. Maybe your error classes exist in the domain module of your project and you'd prefer your icon function to be in the ui module of your project to separate concerns.
So when keeping the sealed classes dumb, it's easy to add new functions and easy to separate them, but it's hard to add new classes of errors because then you need to find and update every function. On the other hand when using polymorphism, it's hard to add new functions and you can't separate them from the class, but it's easy to add new classes.
The benefit of the first (type-checking) example is that the log messages do not have to be hardcoded into the Error subclasses. In this way, clients could potentially log different messages for the same subclass of Error in different parts of an application.
The second (polymorphic) approach assumes everyone wants the same message for each error and that the developer of each subclass knows what that error message should be for all future use cases.
There is an element of flexibility in the first example that does not exist in the second. The previous answer from #Trevor examines the theoretical underpinning of this flexibility.

Kotlin Factory Class with Generic outputs

I'm in the process of trying to port some code I wrote in Java over to Kotlin and I'm struggling mightily with some issues around generics. I quite commonly use a factory pattern in Java to return an instance of a generic interface that I want to call for a given type.
In Java I had this contract:
public Message<T extends Action> {
private List<T> actions;
..some other properties
}
And this interface:
public interface MessageConverter<T extends Action, M extends BaseModel> {
List<M> convertMessage(Message<T> message);
DataType getDataType();
}
And lastly this factory:
public class MessageConverterFactory {
//This gets populated via DI
private Map<DataType, MessageConverter> converterMap;
public <T extends Action, M extends BaseModel> MessageConverter<T, M> getMessageConverter(DataType dataType) {
return converterMap.get(dataType);
}
}
With all that in place, I was able to do things like this:
Message<T> message = mapper.readValue(messageString, type);
MessageConverter<T, M> messageConverter = messageConverterFactory.getMessageConverter(dataType);
List<M> dataModels = messageConverter.convertMessage(message);
I understand that I was abusing raw generic types in Java to an extent to make this happen, but I assumed there would be some way to still do a generic factory pattern like this.
However, no matter with I try with generic variance, star projections, etc. I cannot get Kotlin to accept any version of this code. The closest I got was down to the invocation of the generic converter's convertMessage call. It was failing because I was using star projections and attempting to restrict the type of T, but that was leading to the compiler thinking convertMessage accepts Message<Nothing>.
Is code like this possible in Kotlin? Or is there a similar alternative approach I should be using instead?
Thanks,
Jeff
The literal conversion of this to Kotlin is pretty simple, and the Java-to-Kotlin converter built in to IDEA would spit something like this out almost directly, given the equivalent Java code:
class Message<T: Action> {
private val actions: List<T> = TODO()
...
}
interface MessageConverter<T: Action, out M: BaseModel> {
fun convertMessage(message: Message<T>): List<M>
val dataType: DataType
}
class MessageConverterFactory(val converterMap: Map<DataType, MessageConverter<*, *>>) {
fun <T: Action, M: BaseModel> getMessageConverter(dataType: DataType): MessageConverter<T, M> {
return converterMap[dataType] as MessageConverter<T, M>
}
}
Note, the cast in getMessageConverter -- your Java code is doing the equivalent, without being explicit about it -- I believe the compiler would even spit out a warning about an unchecked assignment.
An alternative in Kotlin is to use an inline function with reified types to return the appropriate converter. For example, something like this:
inline fun <reified T: Action, reified M: BaseModel> converterOf(): MessageConverter<T, M> = when {
T::class == FooAction::class, M::class == BarModel::class -> TODO()
else -> error("No converter available for type ${T::class.simpleName} to ${M::class.simpleName}")
}

How to create compile-time constant in Kotlin from enum?

I have an annotation that requires defaultValue to be compile-time constant. I take defaultValue from enum below:
enum class RaceType {
MARATHON,
SPRINT;
companion object {
fun apply(type: RaceType): RaceDto {
return when (type) {
MARATHON -> MarathonDto()
SPRINT -> SprintDto()
}
}
}
}
My dtos are the following:
interface RaceDto {
}
data class MarathonDto: RaceDto
data class SprintDto: RaceDto
when I use annotation #QraphQLArgument(defaultValue = RaceType.SPRINT.name) Kotlin requires RaceType.SPRINT.name to be compile-time constant.
Annotation implementation itself:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER})
public #interface GraphQLArgument {
String NONE = "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
String NULL = "\n\t\t\n\t\t\n\ue000\ue001\ue002\ue003\n\t\t\t\t\n";
String name();
String description() default "";
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
Class<? extends DefaultValueProvider> defaultValueProvider() default JsonDefaultValueProvider.class;
}
I looked through similar questions but don't see a way how it can be resolved. I also found article related to the topic but nothing worked so far.
Side note: I cannot change annotation since it is from the library and I cannot change the library as well.
To summarize, is there a way to make from enum compile-time constant in Kotlin to use in an annotation?
is there a way to make from enum compile-time constant in Kotlin to use in an annotation?
No, because formally enums aren't compile-time constants in Java.
However please consider the sealed classes:
sealed class RaceType {
object MARATHON: RaceType() {
const val name = "MARATHON" // copy-paste is required here until https://youtrack.jetbrains.com/issue/KT-16304
}
object SPRINT: RaceType()
companion object {
fun apply(type: RaceType): RaceDto {
return when (type) { // the check is in compile time, because of sealed class
MARATHON -> MarathonDto()
SPRINT -> SprintDto()
}
}
}
}
A little part of copy-paste is still required. Please vote on kotlin compiler bug or follow this thread.
However, as I understand, this doesn't solve your issue with #QraphQLArgument(defaultValue = RaceType.SPRINT.name) unfortunately, because the name of class is not the same with value. In the other words, with sealed classes you need to write code to convert input strings to them.

What private constructor in Kotlin for?

I'm a newbie in Kotlin. I want to ask what private constructor in Kotlin for? class DontCreateMe private constructor () { /*...*/ }. I mean what class is supposed to be if we can't create its instance?
Well, the answers in the comments are correct, but since nobody wrote a full answer. I'm going to have a go at it.
Having a private constructor does not necessarily mean that an object cannot be used by external code. It just means that the external code cannot directly use its constructors, so it has to get the instances through an exposed API in the class scope. Since this API is in the class scope, it has access to the private constructor.
The simplest example would be:
class ShyPerson private constructor() {
companion object {
fun goToParty() : ShyPerson {
return ShyPerson()
}
}
}
fun main(args: String) {
// outside code is not directly using the constructor
val person = ShyPerson.goToParty()
// Just so you can see that you have an instance allocated in memory
println(person)
}
The most common use case for this that I've seen is to implement the Singleton pattern, as stated by Mojtaba Haddadi, where the external code can only get access to one instance of the class.
A simple implementation would be:
class Unity private constructor() {
companion object {
private var INSTANCE : Unity? = null
// Note that the reason why I've returned nullable type here is
// because kotlin cannot smart-cast to a non-null type when dealing
// with mutable values (var), because it could have been set to null
// by another thread.
fun instance() : Unity? {
if (INSTANCE == null) {
INSTANCE = Unity()
}
return INSTANCE
}
}
}
fun main(args: Array<String>) {
val instance = Unity.instance()
println(instance)
}
This is often used so that classes that are resource consuming are only instantiated once or so that certain pieces of data are shared by the entire codebase.
Be aware that kotlin uses the object keyword to implement this pattern, with the advantage of being thread-safe. Also some developers consider Singletons to be an anti-pattern
Another use case for private constructors would be to implement Builder patterns, where classes that have complex initialization can be abstracted into a simpler API, so the user doesn't have to deal with clunky constructors. This other answer addresses its uses in kotlin.
One of the simplest uses in real life kotlin code that I've seen is on the Result implementation from the stdlib, where it's being used to change the internal representation of the object.

Kotlin compiler issue with overriding of Java final function in Kotlin

I’m dealing with following issue with Kotlin/Java Compiler.
Imagine following scenario: let First be a Java class with a final function and Second be a Kotlin class extending First with a function of the same name like the final function in First class, example:
// Java class
class First {
final void foo() { }
}
// Kotlin class
class Second: First() {
fun foo() { }
}
Obviously, it’s wrong because the final function foo() can not be overridden. However, compilation pass successfully and in run-time I get java.lang.LinkageError: Method void Second.foo() overrides final method in class First.
Is this correct behavior of compiler? I supposed that there will be some validations for this case. Thank you!