Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Contravariance in Kotlin

I never truly understood generics in Java, so it seems to be the case with Kotlin. Consider the following code snippet (it's a contrived example):

class AnyComparator: Comparator<Any> {
    override fun compare(o1: Any, o2: Any): Int {
        TODO("not implemented")
    }
}


fun <T> test() {
    val x: Comparator<in Double> = AnyComparator() // OK!
    val y: Comparator<in T> = AnyComparator() // Compilation error
}

The second assignment fails with the error

Type mismatch. 
Required: kotlin.Comparator<in T>
Found: AnyComparator

Now, if I understand correctly the in modifier indicates the T is only consumed by the generic type Comparator (it makes contravariant), so I should be able to assign any Comparator with type argument E which is the base class of T. Based on this, I should be able to assign AnyComparator to both variables x and y, since the type Any is the base class of every class in Kotlin. It turns out I can't, and I don't understand why.

like image 894
Katona Avatar asked Jul 01 '17 14:07

Katona


People also ask

What is out in Kotlin?

"Out" keyword is extensively used in Kotlin generics. Its signature looks like this − List<out T> When a type parameter T of a class C is declared out, then C can safely be a super type of C<Derived>. That means, a Number type List can contain double, integer type list.

What is variance in Kotlin?

Variance tells us about the relationship between List<Dog> and List<Animal> where Dog is a subtype of Animal . In Kotlin, dogList can be assigned to Animal list ( val animalList: List<Animal> = dogList ) so the type relation is preserved and List<Dog> is a subtype of List<Animal> . This is called covariance.

What is type parameter in Kotlin?

The type parameter lets you specify exactly that—instead of “This variable holds a list,” you can say something like “This variable holds a list of strings.” Kotlin's syntax for saying “a list of strings” looks the same as in Java: List<String> . You can also declare multiple type parameters for a class.

Are Kotlin classes covariant or contravariant?

They can be made to be covariant or contravariant, depending on the use case required. Kotlin has a declaration-site variance feature that allows developers define the variance of a parameterised type when the class is being defined.

What is contravariance in Java?

Contravariance describes a relationship between two sets of types where they subtype in opposite directions. As an example, we’ll make the simplest of inheritance hierarchies - classes A and B, where B is a subtype of A: And, we’ll create a generic class that can hold objects of our A and B classes.

What is use-site variance in Java and Kotlin?

Developers can use use-site variance in Java and Kotlin to change the behaviour of generic collections. They can be made to be covariant or contravariant, depending on the use case required. Kotlin has a declaration-site variance feature that allows developers define the variance of a parameterised type when the class is being defined.

Is it possible to override method parameters in Kotlin?

Method overriding: In both languages, contravariance is not supported for both the parameters and the return type of an overridden method. Arrays: In both languages, arrays aren’t contravariant at all. Each of the following statements in the snippet below will not compile in Java. The same goes for Kotlin:


1 Answers

It can be seem strange but Any is not the superclass of all kotlin classes but only of not nullable classes. The real superclass of all Kotlin classes is Any? (it is also a superclass of Any).

The generic type T in your test function has no upper bound so it can be a nullable object Any?. The error is because you can't a Comparator<Any> when you need a Comparator<Any?>.

So you can fix your example defining the T upper bound as Any:

fun <T: Any> test() {
    //...
}
like image 175
Fabio Collini Avatar answered Sep 30 '22 09:09

Fabio Collini