In Kotlin, I'm trying to compile the following:
The following code does not compile, because a type is required at:
fun getMapper(animalType: AnimalType): Printer
I tried to use <Any>
or <*>
but got no success. Can someone help?
(easy to see the error by copypasting the code below into https://try.kotlinlang.org)
enum class AnimalType {
CAT, DOG
}
class Dog
class Cat
interface Printer<in T> {
fun mapToString(input: T): String
}
class DogPrinter : Printer<Dog> {
override fun mapToString(input: Dog): String {
return "dog"
}
}
class CatPrinter : Printer<Cat> {
override fun mapToString(input: Cat): String {
return "cat"
}
}
private fun getMapper(animalType: AnimalType): Printer {
return when(animalType) {
AnimalType.CAT -> CatPrinter()
AnimalType.DOG -> DogPrinter()
}
}
fun usage_does_not_compile() {
getMapper(AnimalType.DOG)
.mapToString(5)
}
There are no direct ways to do this in Kotlin. In order to check the generic type, we need to create an instance of the generic class<T> and then we can compare the same with our class.
Generics make a class, interface and, method, consider all (reference) types that are given dynamically as parameters. This ensures type safety. Generic class parameters are specified in angle brackets “<>” after the class name as of the instance variable. Generic constructors are the same as generic methods.
Kotlin allows Interface to have code which means a class can implement an Interface, and inherit the behavior from it. After using Kotlin in Android development for a while, I've just realized the benefit of Interface in Kotlin. In Java 6, Interface can only be used to describe the behaviors, but not implement them.
To achieve that using Kotlin, we need to use the out keyword on the generic type. It means that we can assign this reference to any of its supertypes. The out value can be only be produced by the given class but not consumed: We defined a ParameterizedProducer class that can produce a value of type T.
To create an instance of such a class, simply provide the type arguments: But if the parameters can be inferred, for example, from the constructor arguments, you can omit the type arguments: One of the trickiest aspects of Java's type system is the wildcard types (see Java Generics FAQ ). Kotlin doesn't have these.
Let’s say that we want to create a parameterized class. We can easily do this in Kotlin language by using generic types: We can create an instance of such a class by setting a parameterized type explicitly when using the constructor: Happily, Kotlin can infer the generic type from the parameter type so we can omit that when using the constructor:
Kotlin interfaces are similar to interfaces in Java 8. They can contain definitions of abstract methods as well as implementations of non-abstract methods. However, they cannot contain any state. Meaning, interface may have property but it needs to be abstract or has to provide accessor implementations.
I've modified your code a bit. The getMapper
function is inline
with a reified
generic type now, which makes the call pretty readable at getMapper<Dog>()
.
Read about reified
here: What does the reified keyword in Kotlin really do?
private inline fun <reified T> getMapper(): Printer<T> {
when (T::class) {
Cat::class -> return CatPrinter() as Printer<T>
Dog::class -> return DogPrinter() as Printer<T>
}
throw IllegalArgumentException()
}
fun main(args: Array<String>) {
println(getMapper<Dog>().mapToString(Dog()))
println(getMapper<Cat>().mapToString(Cat()))
}
The reified thing is not even necessary actually but makes the client side more readable. Alternatively, you could also pass in the class as an argument to the getMapper
function. The really important part is to make this one generic.
The unchecked casts are not very cool but seem to be safe here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With