Some Kotlin generics can be expressed in Java, while others are not. Let's start with what can be expressed in Java.
Let's declare a generic function in Kotlin.
// Kotlin
fun <T> convertToString(arg: T) : String {
return arg.toString()
}
Declare a type parameter between the fun
keyword in a normal function declaration and the function name.
Equivalent functions can be expressed in Java.
// Java
<T> String convertToString(T arg) {
return arg.toString();
}
You can constrain type parameters.
fun <T: Exception> convertToString(arg: T) : String {
return arg.localizedMessage
}
The type parameter T
has a constraint that makes it an Exception or its subclass.
This can also be expressed in Java.
// Java
<T extends Exception> String convertToString(T arg) {
return arg.getLocalizedMessage();
}
Let's define a generic class in Kotlin.
class Box<T>(t: T) {
var value = t
}
Declare a type parameter between the class name and the primary constructor. The equivalent class can be expressed in Java.
class Box<T> {
T value;
Box(T t) {
this.value = t;
}
}
You can also constrain type parameters for classes.
class Box<T: Number> (t: T) {
var value = t
}
The type parameter T
is constrained to be Number or its subclass.
This can also be expressed in Java.
static class Box<T extends Number> {
T value;
Box(T t) {
this.value = t;
}
}
where
clauseThe where
clause is useful when you want to specify multiple type constraints in a function or class.
fun <T> doSomething(t: T) where T: Runnable, T: Cancellable {
}
Similar to Java's boundary wildcard type, Kotlin also has a notation for specifying displacement when declaring variables.
Java | Kotlin | |
---|---|---|
Upper bound constraint | <? extends XXX> | <out XXX> |
Lower bound constraint | <? super XXX> | <in XXX> |
Dog is a subtype of Animal.
Therefore, List \
open class Animal
class Dog: Animal()
var animals: List<out Animal> = emptyList()
val dogs: List<Dog> = emptyList()
animals = dogs
Dog is a subtype of Animal.
List \
open class Animal
class Cat: Animal()
val animals: MutableList<Animal> = mutableListOf()
var cats: MutableList<in Cat> = mutableListOf()
cats = animals
In Kotlin, the displacement specification at the time of declaration is called ** type projection **.
In Java, the relationship between covariant and contravariant cannot be specified in the class itself. Therefore, it is necessary to specify the displacement each time the variable declaration etc.
Kotlin, on the other hand, has a mechanism for specifying displacement when declaring a class. There is no risk of displacement being used against the intent of the class designer or implementer.
//↓ Specify out position for type parameter
class Supplier<out T> {
fun get(): T {
//
}
}
The Supplier
class is covariant with respect to the type parameter T.
var animalSupplier: Supplier<Animal> = Supplier()
var dogSupplier: Supplier<Dog> = Supplier()
animalSupplier = dogSupplier
Displacement specification is not required when declaring a variable. Similarly, it is possible to make a contravariant by specifying the ʻin` position.
You can pass null to the following generic functions and arguments.
fun <T> doSomething(t: T){
}
doSomething(null) // OK
The Kotlin type that corresponds to the Java root class ʻObject is ʻAny
.
However, the upper bound for unconstrained type parameters is ʻAny? Instead of ʻAny
.
If you want to declare a non-nullable generic function Specify ʻAny` for the type constraint.
fun <T: Any> doSomething(t: T){
}
doSomething(null) //Compile error
doSomething(1) // OK
doSomething("hoge") // OK
There is a mechanism called star projection that specifies an asterisk *
for the type parameter.
var animals: List<*> = //
The above List <*>
is not equal to List <Any?>
. It behaves as List <out Any?>
.
Star projection is a syntax used when you are not interested in type parameters.
It's safe to take an element and treat it as ʻAny?`, But adding an element to a list is not safe.
As shown below, it behaves as a ʻout` position due to the risk of receiving an unexpected type.
val animals: MutableList<*> = mutableListOf<Animal>()
val animal = animals.first() // OK
animals.add(1) //Compile error
animals.add(Dog()) //Compile error
Recommended Posts