Why can't you access companion object of reified type parameter?

1.3k views Asked by At

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.

1

There are 1 answers

0
Fred On

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.