Generics; Type argument is not within its bounds - kotlin

I've been experimenting with generics lately and found a good instance where I could use them, however, I've become stuck and my searches haven't resulted in a solution, or I've misunderstood. See below code sample:
open class X()
class Y : X()
class Z : X()
abstract class A<T : X> {
lateinit var one: T
fun setup(
one: T
) {
this.one = one
}
}
class B<T : Y> : A<T>()
class C {
fun initB() {
B<Y>() // Works as intended
B<Z>() // Type argument is not within its bounds
B<X>() // Type argument is not within its bounds
}
}
I would like to have a situation whereby accessing one from class B type of one is correctly inferred, so if I instantiate B with type Z then one will be inferred also as type Z. Unfortunately doing this how I thought was the correct way results in 'Type argument is not within its bounds'. Any help would be greatly appreciated.

B's type has to be a subtype of Y as you defined it B<T : Y>, but neither X nor Z is a subtype of Y. X is a supertype of Y, and Z has no vertical connection to Y at all.
Even if they were subtypes, you couldn't do what you were hoping to. Since your class has a T var, T has to be invariant for it to work. Invariant means you cannot implicitly up- and down-cast the type of an instance of the class.
Consider how it would break if it allowed you to:
val bY: B<Y> = B<Y>()
val bX: B<X> = bY // not allowed because of invariance
// If it were allowed:
bX.one = X()
val y: Y = bY.one // ClassCastException: cannot cast X to Y

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

subtypes/supertypes with upper bounded generics

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!

Kotlin: Generic high-order function - Type mismatch

I'm trying to grasp generics in Kotlin.
In the following sample, I'm trying to constrain the type T and use it inside a high-order function, or just functions in general.
interface A {
fun foo()
}
class bar<T : A> (val g: A, val h: T, val callable: (T) -> Unit ) {
fun test() {
// Polymorphism works as expected
g.foo()
h.foo()
// Type mismatch: inferred type is A but T was expected
callable(g)
// Fine
callable(h)
// Type mismatch: inferred type is A but T was expected
baz(g)
// Fine
baz(h)
}
fun baz(l: T) {}
}
Could you please explain why it doesn't compile?
You declared that T must be a supertype of A.
Let's use a more graphic example.
Assume A is a Person and T is a Teacher. You've declared that a Teacher is a Person - which makes sense. However, the opposite is not true. Not all Persons (A) are Teachers (T).
When invoking both bar and callable you expect a Teacher to be passed in.
You cannot simply call these functions with a Person or A, because that person might not be a Teacher (or T).

Kotlin higher order function parameters: Passing subtypes

I have run into a problem with function parameters in Kotlin. I will explain the issue with the help of some code.
I created a class hierarchy. When I pass a subtype into a function expecting a parent type, there is no issue.
open class A (val i: Int)
class B (val j: Int) : A(j)
fun f(x: A){
print(x)
}
fun test_f(){
f(A(1))
f(B(1)) //no problem
}
I tried to mimic this with function parameters.
fun g(x: (A)->Int){
print(x)
}
fun test_g(){
val l1 = { a: A -> a.hashCode()}
g(l1)
val l2 = { b: B -> b.hashCode()}
g(l2) //Error: Type mismatch. Required: (A)->Int, Found: (B)->Int
}
It seems that function type (B) -> Int is not a subtype of (A) -> Int.
What is the best way to address this?
My original problem is to define a higher order function in A.h that takes a function z: (A) -> X as parameter. And I want call h on an object of type B and pass a function z: (B) -> X.
Update:
I tried generics with upper bound, yet my issue is not solved. Please find code below:
// Using generics doesn't allow me to pass A.
open class A (val i: Int) {
fun <M: A> g(x: (M)->Int){
print(x(this)) // Error: Type mismatch. Expected: M, Found: A
}
}
You can solve it using generics and an extension function on a generic receiver. Deriving the extension function from your updated sample:
fun <T : A> T.g(x: (T)->Int){
print(x(this))
}
This way it is ensured that the receiver and the first parameter type of the given function are the same, which is either an A or a subtype of it.
What you're trying to do is a conversion from function type (B) -> Int (source) to (A) -> Int (target). This is not a safe conversion.
Your source function (B) -> Int takes any instance which is a B, but not necessarily an instance of type A. More concretely, it cannot handle all arguments that are of type A but not of type B.
Imagine your classes look like this:
open class A
class B : A {
fun onlyB() = 29
}
You can define a function
val fb: (B) -> Int = { it.onlyB() }
val fa: (A) -> Int = fb // ERROR
The function fb will not be able to operate on class A, since A does not have the onlyB() function. As a consequence, you're not allowed to convert it to a function type which takes A parameters.
This concept is called contravariance, meaning that input parameters can only be constrained by becoming more concrete, not more abstract. So, the opposite direction works:
val fa: (A) -> Int = { it.hashCode() }
val fb: (B) -> Int = fa // OK, every B is also an A
In contrast, for return values, the concept of covariance applies. This means that return values are allowed to become more abstract, but not more concrete:
val fInt: (B) -> Int = { it.onlyB() }
val fNum: (B) -> Number = fInt // OK, every Int is also a Number
These relations can be exploited in generic classes, using Kotlin's in (contravariance) and out (covariance) keywords -- see here for detailed explanation.

a typescript code confusing me

There is a question confusing me so much。
The tutorial on the site(http://www.typescriptlang.org)introduces the grammar as follows:
https://i.stack.imgur.com/3t4ui.png
It means that {name:”Alice”} is the subtype of {name:”Alice”,location:”seattle”}( obviously,has excess property),but the typescript document tells us as follow:
"S is a subtype of a type T, and T is a supertype of S, if S has no excess properties with respect to T"
I believe what it is telling you is that the object y must be of the same type as the object x or at least a sub-type. x is part of y, but not the other way around. This is a more specific example of the differences using classes instead of objects,
class x {
constructor(public name: string){}
}
class y extends x {
constructor(public name: string, public location: string){
super(name);
}
}
let myX: x = y; // OK
let myY: y = x; // Error: "Type 'typeof x' is not assignable to type 'y'. Property 'location' is missing in type 'typeof x'. let myY: y"
// PLUG this code in the typescript playground to see it work, or not... :)