In the below, since T is reified, I want to use it "almost as if it were a normal class" by accessing its companion object.
class Cls {
companion object {
fun method() { }
}
}
inline fun <reified T> func() {
T.method() // error
}
fun main() {
func<Cls>()
}
But fails with
Type parameter 'T' cannot have or inherit a companion object, so it cannot be on the left hand side of dot
So it seems that a significant amount of information is lost. I get the same error with and without reified. I was hoping a reified type parameter would a fuller generic implementation than Java's. I have a ton of experience in C++ templates.
I've found some workarounds (that are all pretty disappointing using reflection), but really I'm asking why this can't work.
I'm not sure this is answering all the questions, but it's too big for a comment.
First and as stated in the comments, the way the code is written, T is not necessarily a Cls so to allow this you'd need some changes:
open class Cls {
companion object {
fun method() { }
}
}
inline fun <reified T : Cls> func() {
}
open the class and let Kotlin know T is a Cls
However, even though it's inlined, this still wouldn't let you call the companion method because T has no companion. Even without generics:
open class Cls {
companion object {
fun method() { }
}
}
class Foo : Cls
fun main() = Foo.method() // doesn't work
Doesn't work because companions are not inherited. Why? It was a conscious decision by the Kotlin designers. As you know Kotlin aims to correct a lot of issues Java had and this was one.
Static methods in Java are bound at compile-time while overriding is based on dynamic binding at runtime. This becomes quite confusing when you mix both and Kotlin tried to avoid this. Here's an example:
class Cls {
public static void method() {
System.out.println("Cls' method");
}
}
class Foo extends Cls {
public static void method() {
System.out.println("Foo's method");
}
}
public class Main {
public static void main(String[] args) {
Cls parent = new Foo();
parent.method();
}
}
If method would truly be overridden it would print out Foo's method, but indeed this prints Cls' method. The reason is that there's no overriding, but there's shadowing happening. On the other hand, if the methods wouldn't be static, then you'd get Foo's method since it is indeed overridden. This apparently caused confusion amongst developers and Kotlin completely disallowed it.
As C++ programmer for more than 20 years, and a Kotlin programmer for few years, I was also puzzled by this. The answer is simple: It is the error message that is confusing. Is should have simply said "Unresolved reference". Exactly as it does here:
inline fun <reified T> func(t: T) {
t.method() // unresolved reference
}
With C++, we can write almost anything in the template and it will be compiled where being called, in the context of the concrete template argument. It is almost like a preprocessor directive. That is not the case with Kotlin generics. With Kotlin generics everything needs to be syntactically correct with any T that is acceptable by the generic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With