Why it is forbidden to use 'out' keyword in generics if a method excepts the type parameter as a parameter? - kotlin

I'm looking for an example that can cause a problem when using out in class declaration and the class has a method that get the parameter type as argument.
Also I'm looking for an example that can cause a problem when using in in class declaration and the parameter type is a var member of the class?
I think that i will be able to understand the rules only by examples

Suppose these are the classes we are working with:
open class Animal
class Cat: Animal() {
fun meow() = println("meow")
}
If we create a class like this with covariant out type and the compiler allowed us to use the type as a function parameter:
class Foo<out T: Animal> {
private var animal: T? = null
fun consumeValue(x: T) { // NOT ALLOWED
animal = x
}
fun produceValue(): T? {
return animal
}
}
Then if you do this, it will be lead to an impossible situation where we are trying to call meow on an Animal that doesn't have a meow function:
val catConsumer = Foo<Cat>()
val animalConsumer: Foo<Animal> = catConsumer // upcasting is valid for covariant type
animalConsumer.consumeValue(Animal())
catConsumer.produceValue()?.meow() // can't call `meow` on plain Animal
And if we create a class like this with contravariant in type and the compiler allowed us to use the type as a return value:
class Bar<in T: Animal>(private val library: List<T>) {
fun produceValue(): T { // NOT ALLOWED
return library.random()
}
}
Then if you do this, it will lead to the compiler impossibly casting a return type to a subtype.
val animalProducer: Bar<Animal> = Bar(List(5) { Animal() })
val catProducer: Bar<Cat> = animalProducer // downcasting is valid for contravariant type
catProducer.produceValue().meow() // can't call `meow` on plain Animal
A property has a getter which is just like a function that returns a value, and a var property additionally has a setter, which is just like a function that takes a parameter. So val properties are not compatible with contravariance (in) and var properties are not compatible with contravariance or covariance (out). Private properties aren't encumbered by these restrictions because within the class's inner workings, the type is invariant. All the class can know about its own type is its bounds. Variance just affects how the class can be cast (viewed) by the outside world.
So an example with val is enough to show why any property is incompatible with contravariance. You could replace val with var below and it would be no different.
class Bar<in T: Animal>(
val animal: T // NOT ALLOWED
)
val animalProducer: Bar<Animal> = Bar(Animal())
val catProducer: Bar<Cat> = animalProducer // downcasting is valid for contravariant type
catProducer.animal.meow() // can't call `meow` on plain Animal

Small reminder about variance
When you have a generic class G<T> (parameterized type), the variance is about defining a relationship between the hierarchy of the types G<T> for different Ts, and the hierarchy of the different Ts themselves.
For instance, if child class C extends a parent P then:
does List<C> extend List<P>? (List<T> would be covariant in T)
or the reverse? (contravariant)
or is there no relationship between List<C> and List<P>? (invariant).
Example
Now, consider List<out T>, which means that List is covariant in T.
As we've just seen, declaring list as such means that the following holds: "if C extends P, then List<C> extends List<P>".
Let's assume the following class declarations here:
open class Parent {
fun doParentStuff()
}
class Child : Parent() {
fun doChildStuff()
}
The covariance of List<out T> means that this is possible:
val listOfChild: List<Child> = listOf<Child>(Child(), Child())
// this is ok because List is covariant in T (out T)
// so List<Child> is a subtype of List<Parent>, and can be assigned to listOfParent
val listOfParent: List<Parent> = listOfChild
So what would happen if we could declare a method in the List class that accepts a parameter T?
class List<out T> {
fun add(element: T) {
// I can guarantee here that I have an instance of T, right?
}
}
The rules of most languages (including Kotlin) state that if a method accepts a parameter of type T, you can technically get an instance of T or any subclass of T (this is the point of subclassing), but you have at least all the API of T available to you.
But remember that we declared List<out T>, which means I can do:
val listOfChild: List<Child> = listOf<Child>(Child(), Child())
// this is ok because List is covariant in T (out T)
val listOfParent: List<Parent> = listOfChild
// listOfChild and listOfParent point to the same list instance
// so here we are effectively adding a Parent instance to the listOfChild
listOfParent.add(Parent())
// oops, the last one is not an instance of Child, bad things will happen here
// we could fail right here at runtime because Parent cannot be cast to Child
val child: Child = listOfChild.last
// even worse, look at what looks possible, but is not:
child.doChildThing()
Here you can see that from within the List<Child> instance, we actually could receive an instance of Parent which is not a subclass of Child in a method that had declared a parameter of type Child.

Related

Why is the no lateinit block in Kotlin?

The following code is valid Kotlin code:
abstract class A {
protected lateinit var v: X
abstract fun f(): X
class SubA : A() {
override fun f(): X {
return SubX()
}
init {
v = f()
}
}
}
It defines an abstract class which has a lateinit var field and an abstract method that sets the value of that field. The reason behind this is that that method may be called later again, and its behavior should be defined in the subclasses that extend the original class.
This code is a simplification of a real-world code, and even though it works, I feel like it is messy since the developer of the subclass could choose not to (or forget) to call v = f() inside an init block. And we cannot do that in A either because then it will show a warning that we are calling a non-final method in the constructor. What I propose is the following:
abstract class A {
private lateinit var v: X
abstract fun f(): X
class SubA : A() {
override fun f(): X {
return SubX()
}
}
lateinit { // this does not exist
v = f()
}
}
The benefits of this is that now the field can be private instead of protected, and the developer does not have to manually call v = f() in each of their subclasses (or the subclasses of their subclasses), and the naming fits with the nomenclature of Kotlin since lateinit is already a keyword and init is already a block. The only difference between an init and a lateinit block would be that the contents of a lateinit block are executed after the subclass constructors, not before like init.
My question is, why isn't this a thing? Is this already possible with some other syntax that I do not know about? If not, do you think it's something that should be added to Kotlin? How and where can I make this suggestion so that the developers would most likely see it?
There are three options, and you can implement your lateinit block in two ways
don't lazy init - just have a normal construction parameter
use a delegated lazy property
add a lambda construction parameter to the superclass class A
All of these solves the problem of requiring subclasses of A having to perform some initialization task. The behaviour is encapsulated within class A.
Normal construction parameter
Normally I'd prefer this approach, and don't lazy init. It's usually not needed.
abstract class A(val v: X)
class SubA : A(SubX())
interface X
class SubX : X
fun f() can be replaced entirely by val v.
This has many advantages, primarily that it's easier to understand, manage because it's immutable, and update as your application changes.
Delegated lazy property
Assuming lazy initialization is required, and based on the example you've provided, I prefer the delegated lazy property approach.
The existing equivalent of your proposed lateinit block is a lazy property.
abstract class A {
protected val v: X by lazy { f() }
abstract fun f(): X
}
class SubA : A() {
override fun f(): X {
return SubX()
}
}
interface X
class SubX : X
The superclass can simply call the function f() from within the lazy {} block.
The lazy block will only run once, if it is required.
Construction parameter
Alternatively the superclass can define a lambda as construction parameter, which returns an X.
Using a lambda as a construction parameter might be preferred if the providers are independent of implementations of class A, so they can be defined separately, which helps with testing and re-used.
fun interface ValueProvider : () -> X
abstract class A(
private val valueProvider: ValueProvider
) {
protected val v: X get() = valueProvider()
}
class SubA : A(ValueProvider { SubX() })
interface X
class SubX : X
The construction parameter replaces the need for fun f().
To make things crystal clear I've also defined the lambda as ValueProvider. This also makes it easier to find usages, and to define some KDoc on it.
For some variety, I haven't used a lazy delegate here. Because val v has a getter defined (get() = ...), valueProvider will always be invoked. But, if needed, a lazy property can be used again.
abstract class A(
private val valueProvider: ValueProvider
) {
protected val v: X by lazy(valueProvider)
}

Use-site vs declaration-site difference in type projections in Kotlin

Type Hierarchy
open class Fruit()
open class CitrusFruit : Fruit()
class Orange : CitrusFruit()
Declaration-site Variance
The Crate is used as a producer or consumer of Fruits.
Invariant class
class Crate<T>(private val elements: MutableList<T>) {
fun add(t: T) = elements.add(t) // Consumer allowed
fun last(): T = elements.last() // Producer allowed
}
Covariant classout
class Crate<out T>(private val elements: MutableList<T>) {
fun add(t: T) = elements.add(t) // Consumer not allowed: Error
fun last(): T = elements.last() // Producer allowed
}
Contravariant classin
class Crate<in T>(private val elements: MutableList<T>) {
fun add(t: T) = elements.add(t) // Consumer allowed
fun last(): T = elements.last() // Producer not allowed: Error
}
Use-site Variance
All these use-site projections are for the invariant class Crate<T> defined above.
No Projection
No subtyping allowed: Only the Crate<Fruit> can be assigned to a Crate<Fruit>.
fun main() {
val invariantCrate: Crate<Fruit> = Crate<Fruit>(mutableListOf(Fruit(), Orange()))
invariantCrate.add(Orange()) // Consumer allowed
invariantCrate.last() // Producer allowed
}
Covariant Projectionout
Subtyping allowed: Crate<CitrusFruit> can be assigned to Crate<Fruit> when CitrusFruit is a subtype of Fruit.
fun main() {
val covariantCrate: Crate<out Fruit> = Crate<CitrusFruit>(mutableListOf(Orange()))
covariantCrate.add(Orange()) // Consumer not allowed: Error
covariantCrate.last() // Producer allowed
}
Contravariant Projectionin
Subtyping allowed: Crate<CitrusFruit> can be assigned to Crate<Orange> when the CitrusFruit is a supertype of Orange.
fun main() {
val contravariantCrate: Crate<in Orange> = Crate<CitrusFruit>(mutableListOf(Orange()))
contravariantCrate.add(Orange()) // Consumer allowed
contravariantCrate.last() // Producer allowed: No Error?
}
Questions
Is my understanding and the use of type projection correct in the given example?
For contravariance: why is the last()(producer) function not allowed at declaration-site but allowed at use-site? Shouldn't the compiler show an error like it shows in the declaration-site example? Maybe I'm missing something? If the producer is allowed for contravariance only at use-site, what could be the use case for it?
I prefer detailed answers with examples but any kind input will be much appreciated.
Let's start with the use-site.
When you write
val contravariantCrate: Crate<in Orange> = ...
the right side could be a Crate<Orange>, Crate<Fruit>, Crate<Any?>, etc. So the basic rule is that any use of contravariantCrate should work if it had any of these types.
In particular, for all of them
contravariantCrate.last()
is legal (with type Orange, Fruit, and Any? respectively). So it's legal for Crate<in Orange> and has type Any?.
Similarly for covariantCrate; calling the consumer method technically is allowed, just not with Orange. The problem is that a Crate<Nothing> is a Crate<out Fruit>, and you couldn't do
val covariantCrate: Crate<Nothing> = ...
covariantCrate.add(Orange())
Instead the parameter type is the greatest common subtype of Fruit, CitrusFruit, Nothing, etc. which is Nothing. And
covariantCrate.add(TODO())
is indeed legal because the return type of TODO() is Nothing (but will give warnings about unreachable code).
Declaration-site in or out effectively say that all uses are in/out. So for a contravariant class Crate<in T>, all calls to last() return Any?. So you should just declare it with that type.
My guess is that the difference between declaration-site and use-site contravariance is that delcaration-site can be statically checked by the compiler, but when using projections there is always the original, unprojected object in existence at run-time. Therefore, it is not possible to prevent the creation of the producer methods for in projections.
When you write:
class Crate<in T>(private val elements: MutableList<T>) {
fun add(t: T) = elements.add(t) // Consumer allowed
fun last(): T = elements.last() // Producer not allowed: Error
}
The compiler can know at compile-time that no method on Crate<T> should exist that produces a T, so the definition of fun last(): T is invalid.
But when you write:
val contravariantCrate: Crate<in Orange> = Crate<CitrusFruit>(mutableListOf(Orange()))
What has actually been created is a Crate<Any?>, because generics are erased by the compiler. Although you specified that you don't care about producing an item, the generic-erased Crate object still exists with the fun last(): Any? method.
One would expect the projected method to be fun last(): Nothing, in order to give you a compiler-time error if you try to call it. Perhaps that is not possible because of the need for the object to exist, and therefore be able to return something from the last() method.

Instantiating classes from non-reified type parameters

I'm building an ORM for use with jasync-sql in Kotlin and there's a fundamental problem that I can't solve. I think it boils down to:
How can one instantiate an instance of a class of type T, given a
non-reified type parameter T?
The well known Spring Data project manages this and you can see it in their CrudRepository<T, ID> interface that is parameterised with a type parameter T and exposes methods that return instances of type T. I've had a look through the source without much success but somewhere it must be able to instantiate a class of type T at runtime, despite the fact that T is being erased.
When I look at my own AbstractRepository<T> abstract class, I can't work out how to get a reference to the constructor of T as it requires accessing T::class.constructors which understandably fails unless T is a reified type. Given that one can only used reified types in the parameters of inline functions, I'm a bit lost as to how this can work?
On the JVM, runtime types of objects are erased, but generic types on classes aren't. So if you're working with concrete specializations, you can use reflection to retrieve the type parameter:
import java.lang.reflect.*
​
abstract class AbstractRepository<T>
​
#Suppress("UNCHECKED_CAST")
fun <T> Class<out AbstractRepository<T>>.repositoryType(): Class<T> =
generateSequence<Type>(this) {
(it as? Class<*> ?: (it as? ParameterizedType)?.rawType as? Class<*>)
?.genericSuperclass
}
.filterIsInstance<ParameterizedType>()
.first { it.rawType == AbstractRepository::class.java }
.actualTypeArguments
.single() as Class<T>
​
class IntRepository : AbstractRepository<Int>()
class StringRepository : AbstractRepository<String>()
interface Foo
class FooRepository : AbstractRepository<Foo>()
class Bar
class BarRepository : AbstractRepository<Bar>()
​
fun main() {
println(IntRepository::class.java.repositoryType())
println(StringRepository::class.java.repositoryType())
println(FooRepository::class.java.repositoryType())
println(BarRepository::class.java.repositoryType())
}
class java.lang.Integer
class java.lang.String
interface Foo
class Bar
In your own CrudRepository you can add a companion object with an inline fun which is responsible to instantiate your repository by passing to it the corresponding class.
class MyCrudRepository<T> protected constructor(
private val type: Class<T>,
) {
companion object {
inline fun <reified T : Any> of() = MyCrudRepository(T::class.java)
}
fun createTypeInstance() = type::class.createInstance()
}

Getting class of lateinit property in Kotlin

Is it somehow possible to get ::class.java from Kotlin lateinit property before it is initialized?
Logically it should work - I'm trying to obtain a class not a value, but in reality it fails with uninitialized property access exception.
Note that the property I'm trying to get class of is in generic class and its type is one of generic parameters:
abstract class MVIFragment<
out INTERACTOR : MVIInteractor<UINTERFACE>,
UINTERFACE : MVIUIInterface,
MODEL : MVIViewModel
>
: Fragment(), MVIUIInterface, KodeinAware {
lateinit var viewModel: MODEL
I need the class to create an instance of ViewModel
viewModel = ViewModelProviders.of(this).get(viewModel::class.java)
Of course I can't do:
viewModel = ViewModelProviders.of(this).get(MODEL::class.java)
Any solution for that?
Due to type erasure, generic types are not known at runtime. That's just how Java/JVM works, and Kotlin doesn't attempt to magically work around it. (Unlike Scala, which has implicit magic which works magically, except when it doesn't.)
You will have to pass it along from some context where the type is statically determined, e.g.
class Container<T : Any>(private val tClass: Class<T>) {
val t: T = tClass.newInstance()
}
Container(String::class.java)
You can use an inline function with reified types to hide this ugliness,
class Container<T : Any>(private val tClass: Class<T>) {
val t: T = tClass.newInstance()
companion object {
inline operator fun <reified T : Any> invoke() = Container(T::class.java)
}
}
Container<String>()
which really compiles to the same thing. (The <String> can be omitted if type inference can determine it from context.)
In your case, it won't be possible to do this trick in the base (abstract) class; it has to be done on the concrete types.

What is the purpose of having bound class reference return a covariant type?

I'm playing with reflection and I came out with this problem. When using bound class reference via the ::class syntax, I get a covariant KClass type:
fun <T> foo(entry: T) {
with(entry::class) {
this // is instance of KClass<out T>
}
}
As I could learn from the docs, this will return the exact type of the object, in case it is instance of a subtype of T, hence the variance modifier.
However this prevents retrieving properties declared in the T class and getting their value (which is what I'm trying to do)
fun <T> foo(entry: T) {
with(entry::class) {
for (prop in memberProperties) {
val v = prop.get(entry) //compile error: I can't consume T
}
}
}
I found that a solution is using javaClass.kotlin extension function on the object reference, to get instead the invariant type:
fun <T> foo(entry: T) {
with(entry.javaClass.kotlin) {
this // is instance of KClass<T>
}
}
This way, I get both the exact type at runtime and the possibility to consume the type.
Interestingly, if I use a supertype instead of a generic, with the latter method I still get access to the correct type, without the need of variance:
class Derived: Base()
fun foo(entry: Base) {
with(entry.javaClass.kotlin) {
println(this == Derived::class)
}
}
fun main(args: Array<String>) {
val derived = Derived()
foo(derived) // prints 'true'
}
If I got it correct, ::class is equal to calling the java getClass, which returns a variant type with a wildcard, while javaClass is a getClass with a cast to the specific type.
Still, I don't get why would I ever need a covariant KClass, when it limits me to only produce the type, given that there are other ways to access the exact class at runtime and use it freely, and I wonder if the more immediate ::class should return an invariant type by design.
The reason for covariance in bound ::class references is, the actual runtime type of an object the expression is evaluated to might differ from the declared or inferred type of the expression.
Example:
open class Base
class Derived : Base()
fun someBase(): Base = Derived()
val kClass = someBase()::class
The expression someBase() is typed as Base, but at runtime it's a Derived object that it gets evaluated to.
Typing someBase()::class as invariant KClass<Base> is simply incorrect, in fact, the actuall result of evaluating this expression is KClass<Derived>.
To solve this possible inconsistency (that would lead to broken type-safety), all bound class references are covariant: someBase()::class is KClass<out Base>, meaning that at runtime someBase() might be a subtype of Base, and therefore this might be a class token of a subtype of Base.
This is, of course, not the case with unbound class references: when you take Base::class, you know for sure that it's the class token of Base and not of some of its subtypes, so it's invariant KClass<Base>.