Reified type parameters in classes

Just as the title says. I’m suggesting the ability to use the reified keyword in the type parameters list of a class.

Which of course means that class couldn’t be instantiated using a non-reified type parameter. But that’s just like functions with reified type parameters.

(One) use case:

abstract class AbstractStreamable<out T>: Streamable<T>
{
    inline protected fun <reified Self: Streamable<@UnsafeVariance T>>
    equalsImpl(other: Any?): Boolean
        =  this === other
        || other != null
        && other is Self
        && zip(stream(), other.stream()).all { it.first == it.second }

    override fun equals(other: Any?)
        = equalsImpl<Streamable<T>>(other)

    override fun hashCode()
        = ...

}

Now, whenever I want a subclass to use a Self type which is not Streamable<T>, I need to override equals. This is annoying because overriding equals but not hashcode yields a warning. It would be much simpler/terse to just pass the self type to AbstractStreamable in the superclass declaration.

Don’t focus on the example too much though, I know it’s pretty flawed (like it breaks the equals contract, see here).

Is this a possibility? Why/why not?

1 Like

This is in a way something that’s in the works for one of the future versions of Java (I’d expect it for Java 11, but may be a miracle occurs, and it will be Java 10), under the name of Project Valhalla. Reified generics are a lot harder on classes than on inline functions, because, well, classes are not inlined. When Java gets class specialization, Kotlin will get it too, of course. Before that —I’m not sure.

2 Likes

Would something be wrong by transforming:

class Test<reified X> {}

into:

class Test<X> (val klass: Class<X>) {}

and then acting as though every function within the class had the reified X type parameter available?

Yes, the way things are stored (object references vs primitives) will be wrong if X is Int, for example. Please refer to the Valhalla docs I mentioned above.

I’m confused, primitive types cannot (currently) be type parameters (even reified – or maybe this is where I’m wrong?).

Maybe you thought I was commenting on Valhalla?

Sorry, I’ve confused you, should have provided more details.

In today’s Kotlin, we can say List<Int>. It does not translate to an actual list of primitive Int’s, but is implemented through boxing, you are right. We could probably design the interaction between reified functions and classes that work on boxed values, but preserve type information at runtime. That would be a lot of work, would be incompatible with Valhalla, wouldn’t solve any of the performance issues related to boxing, would also mess up Java APIs quite a bit (passing those runtime types as extra parameters everywhere). So, it doesn’t look too reasonable for us at the moment to do it in this half-hearted way.

There has been recently an idea of keeping the type information outside the objects, in some kind of a map. It would be more expensive, but a lot less intrusive, and probably much easier to implement. We could at one point get around to experimenting with it, but no ETA as of now.

1 Like

Definitely it would be very nice to have in Kotlin the cure from the old Java illness which name is type erasure. Something similar to Scala Manifest.
And there are plenty of use cases for this feature. Say in case when class C[T] with type parameter T needs to reflect it at run-time to extract its members or construct its instances. Or in case of testing whether object is instance of parameterized type. And in others effects of type erasure drawback.

1 Like