subtypes/supertypes with upper bounded generics - kotlin

learning kotlin & have been reading about subtypes/supertypes and variance from https://typealias.com/guides/star-projections-and-how-they-work/ & generally, this website. I have a question that I don't think is covered (or maybe I'm just confused). Suppose you have a
open class A
open class B : A()
Pretty clearly A is a supertype of B. But what about the following?
open class Foo<T : A> {
fun doSomething(temp: T)
}
open class SubFoo : Foo<B>() {
}
Is SubFoo a subtype of Foo?
fun input(input: Foo<A>)
fun output(): SubFoo<B>
val inputParam = SubFoo()
input(inputParam) // works?
val ret: Foo<A> = output() // also works??
Intuitively I think the above works as desired, and the answer to the above question is yes. But I'm not completely sure, nor do I have a concrete explanation other than it resolves in my head. Honestly there's like 3 things going on here, the typing of A/B, the typing of Foo vs SubFoo, and upper bounding, and I think I'm getting lost in it all. Thanks in advance!!

Is SubFoo a subtype of Foo?
No because Foo is not a type. Foo<A> and Foo<B> are types. Syntactically, Foo on its own is malformed unless the type parameter (the thing that goes in the <>) can be inferred.
In this case, SubFoo is a subtype of Foo<B> because it inherits from Foo<B>. SubFoo does not become a subtype of Foo<A> as a result of this though, so these do not work:
open class SubFoo : Foo<B>() {
override doSomething(temp: B) {
// do something that is specific to B
}
}
fun input(input: Foo<A>) { }
fun output(): SubFoo = SubFoo()
val inputParam = SubFoo()
input(inputParam) // compiler error
val ret: Foo<A> = output() // compiler error
The idea that you also become the subtype of SomeGenericType<SuperType> by inheriting SomeGenericType<Subtype> is called covariance. You can make a type parameter of covariant by adding out to it. For example, List<T> is declared like this:
public interface List<out E>
So List<String> is a subtype of List<Any>.
However, this only works if it is safe to do so. In the case of Foo, it is not safe at all for its type parameter to be covariant. Consider what would happen if val ret: Foo<A> = output() were allowed. I could do:
open class C : A()
val ret: Foo<A> = output() // suppose this worked
ret.doSomething(C())
From the type checker's perspective, this looks all fine. ret is a Foo<A>, so its doSomething takes an A. C inherits from A, so it can be passed to an A parameter.
But what actually happens when this is run? A SubFoo is returned by output(), and SubFoo only accepts Bs in its doSomething method. Oopsies!

Related

Kotlin Type Mismatch "Nothing"

Consider the following code, which has been stripped back to illustrate only the problem with generics:
interface Node
interface GenericNode<T : GenericNode<T>> : Node {
val pointer: NodePtr<T>?
}
class NodePtr<T : Node>(private val value: T) {
fun isPointingTo(other: T): Boolean {
return value == other
}
}
class BasicNode : Node
class GenericNodeImpl(override val pointer: NodePtr<GenericNodeImpl>) : GenericNode<GenericNodeImpl>
Node may have many implementations. Here we have GenericNode<T : GenericNode<T>> which contains a pointer to another GenericNode<T : GenericNode<T>> (consider this sort of like a singly-linked list mechanism), and we have BasicNode.
Now consider the following which demonstrates the problem:
fun main(args: Array<String>) {
val a = GenericNodeImpl(null)
val b = GenericNodeImpl(NodePtr(a))
val c = GenericNodeImpl(NodePtr(b))
val d = BasicNode()
val list: List<Node> = listOf(a, b, c, d)
list.filterIsInstance<GenericNode<*>>().filter { it.pointer?.isPointingTo(a) ?: false }
}
I've declared List<Node> as it can contain any Node type, but then I want to filter any instances of type GenericNode<*>. I don't care about the specific implementation, or what T is in this case, it just has to be GenericNode<*>.
For each of those nodes, I want to know which ones are pointing to a, but isPointingTo(a) contains the following error:
Type mismatch.
Required: Nothing
Found: GenericNodeImpl
I'm assuming that the issue is caused by filtering GenericNode<*> where * is Unknown, but that is unavoidable. Is there an in or out missing somewhere?
Technically NodePtr could be contravariant in T (in T) because it only takes a T as input (to the isPointingTo() method).
As a consequence, GenericNode could also be in T, because T is only used for the pointer property which is now in T.
That being said, it doesn't solve your problem, because your problem is conceptual. You're filtering with GenericNode<*>, meaning you don't know what T is, and T could really be any subtype of GenericNode, including Nothing. This means that the following NothingGenericNode implementation could be a potential element in the list:
class NothingGenericNode(override val pointer: NodePtr<Nothing>?) : GenericNode<Nothing>
From there, you can see that pointer could be of type NodePtr<Nothing> and thus accept 0 possible values as argument of isPointingTo. The compiler protects you from that. It only allows you to pass arguments that would be valid for all possible subtypes of GenericNode. But since there is a subtype that accepts nothing at all, then your generic code cannot pass anything either.
One solution to this problem would be to be more lenient on what isPointingTo accepts. For instance it could maybe accept any subtype of Node instead of only the specific T:
fun isPointingTo(other: Node): Boolean

kotlin parent class has var that depend on abstract var

class C(val string: String) {
init {
println(string)
}
}
abstract class A {
abstract var string: String
val c = C(string)
}
class B : A() {
override var string = "string"
}
fun main() {
B()
}
kotlin playground for the problem
This code crash in runtime due to string var not initialized, how to do it right?
It's not a good practice and dangerous to use an abstract or open variable in the initialization of your class. And if you write this code in Android Studio or IntelliJ IDEA you will get this warning: Accessing non-final property string in constructor.
So what's happening here ? Well the super class which is A is going to be initialized first before totally initializing B, so this line of code val c = C(string) is going to run before even giving a value to string and that's what causing the error and you will get a NullPointerException because string is null.
How to fix this ? You can use lazy to initialize c like that:
val c by lazy { C(string) }
Now c is not going to be initialized only if you call it, so now it's safe
because you can't call it only if B is fully initialized.
You are initialising A's properties using non-final properties - in this case, you are initialising c with the abstract property string.
abstract var string: String
val c = C(string)
This in general could be unsafe. Subclasses could override the non-final property in such a way that it is initialised at a later point, which means any initialisation that depends on the non-final property in the superclass will get an undefined value.
In this case, this is exactly what happens. B overrides string so that it is initialised after A's primary constructor is called. As a result, when A's primary constructor is run, and c is initialised, string has the value of null.
To fix this, you can either make c lazy:
val c by lazy { C(string) }
This will only initialise c when you first access it, with whatever the value of string is at that time.
Alternatively, make c computed:
val c get() = C(string)
This will make a new C every time you access c, with the current value of string.

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)
}

Access Implementation's property on variable of type Interface

I'm trying to access the delegate of the property (id) of a class (FooImpl). The problem is, this class implements an interface (Foo), and the property in question overrides a property of this interface. The delegate only exists in the class (not that it could exist in the interface).
The problem is that using the :: operator on a variable of type Foo always returns the property of Foo, not that of the actual instance. The problem in code:
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible
interface Foo {
val id: Int
}
class FooImpl(
id: Int,
) : Foo {
override val id: Int by lazy { id }
}
val <T> KProperty<T>.hasDelegate: Boolean
get() = apply { isAccessible = true }.let { (it as KProperty0<T>).getDelegate() != null }
fun main() {
val foo: Foo = FooImpl(1)
println("foo::id.hasDelegate = ${foo::id.hasDelegate}")
println("(foo as FooImpl)::id.hasDelegate = ${(foo as FooImpl)::id.hasDelegate}")
}
This prints:
foo::id.hasDelegate = false
(foo as FooImpl)::id.hasDelegate = true
But this requires compile-time knowledge of the correct implementation. What I'm looking for is accessing the correct propert without having to specify FooImpl there.
The information is present at runtime because the least (!) intrusive workaround I have found so far is adding fun idProp(): KProperty0<*> to Foo and override fun idProp() = ::id to FooImpl and accessing the property using that.
Is there any better way than that?
I came up with this, but I don't know if there's a better way. The problem to work around is that getDelegate() has to return an actual instance of the delegate, so you need an instance of the class to be able to retrieve a delegate instance. It would really be nice if there was a hasDelegate property built in. Your version of hasDelegate will crash from the cast on unbound KProperty1's, which is all we have to work with when the specific class is unknown.
So to retrieve the delegate instance, we need to do search the class instance's member properties by name, which gives us a KProperty with covariant class type of the super-class type. Since it's covariant, we can call a consuming function like getDelegate() without casting to the invariant type. I think this logically should be safe, since we are passing an instance that we know has the matching type for the ::class that we retrieved the property with.
#Suppress("UNCHECKED_CAST")
fun <T: Any> KProperty1<T, *>.isDelegated(instance: T): Boolean =
(instance::class.memberProperties.first { it.name == name } as KProperty1<T, *>).run {
isAccessible = true
getDelegate(instance) != null
}
fun main() {
val foo: Foo = Foo2()
println("foo::id.hasDelegate = ${Foo::id.isDelegated(foo)}")
}
The problem here is that the owner of the property is resolved on compile time, not on runtime. When you do foo::id then foo (so FooImpl) become its bound receiver, but owner is still resolved to Foo. To fix this we wound need to "cast" property to another owner. Unfortunately, I didn't find a straightforward way to do this.
One solution I found is to use foo::class instead of foo::id as it resolves KClass on runtime, not on compile time. Then I came up with almost exactly the same code as #Tenfour04.
But if you don't mind using Kotlin internals that are public and not protected with any annotation, you can use much cleaner solution:
val KProperty0<*>.hasDelegate: Boolean
get() = apply { isAccessible = true }.getDelegate() != null
fun KProperty0<*>.castToRuntimeType(): KProperty0<*> {
require(this is PropertyReference0)
return PropertyReference0Impl(boundReceiver, boundReceiver::class.java, name, signature, 0)
}
fun main() {
val foo: Foo = FooImpl(1)
println(foo::id.castToRuntimeType().hasDelegate) // true
}
We basically create a new instance of KProperty, copying all its data, but changing the owner to the same type as its bound receiver. As a result, we "cast" it to the runtime type. This is much simpler and it is also cleaner because we separated property casting and checking for a delegate.
Unfortunately, I think Kotlin reflection API is still missing a lot of features. There should be hasDelegate() function, so we don't have to provide receivers, which is not really needed to check if property is delegated. It should be possible to cast KProperty to another type. It should be possible to create bound properties with some API call. But first of all, it should be possible to do something like: Foo::id(foo), so create KProperty of the runtime type of foo. And so on.

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

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.