I am most familiar with Java type erasure (with all its issues and benefits). I have some limited exposure to the expanded possibilities with Kotlin's type system, but I don't have a clear understanding of how type reification works on an erasure-oriented JVM. What is type reification, how does Kotlin make it possible on the JVM, and how does this differ from Java's type erasure and Scala's sophisticated type system?
Type reification is one of Kotlin's tricks. It happens only in inlined generic functions, if you declare the generic parameter as reified
.
Since it's inlined, the generic parameter can be a concrete class
, instead of just a compile-time type information.
You can do something impossible in Java like:
You can now use instanceof
s (in Kotlin, is
s):
inline fun <reified T> f(a: Any) {
if (a is T) println("Hi!")
}
This is obviously impossible in Java.
You're possible to get the java java.lang.Class<T>
instance from the generic parameter now.
inline fun <reified T> f(a: Any) {
println("Hey! my class is ${T::class.java}!")
if (a.javaClass == T::class.java) println("Hi!")
}
Also, KClass
as well:
inline fun <reified T> f(a: Any) {
println("KClass: ${T::class}")
}
You can create instances with the empty constructor:
inline fun <reified T> f(a: Any) {
val o: T = T::class.java.newInstance()
}
Only reified
generic parameter is possible to be passed to other reified
functions.
inline fun <reified T> f(a: Any) {
g<T>(a)
}
inline fun <reified T> g(a: Any) {
if (a is T) println("Bingo!")
}
This is impossible in Kotlin:
inline fun <reified T> f(a: Any) {
}
fun <T> g(a: Any) {
f<T>(a) // error
}
If you use other languages to invoke a reified
inline function in Kotlin, the function parameter will be java.lang.Object
.
You can't use other languages to invoke a reified
function.
Like, if we have a reified function in A.kt
:
inline fun <reified T> f(a: T) = println(T::class.java)
And get it using reflection (it will be compiled as private):
Method method = AKt.class.getDeclaredMethod("f", Object.class);
This code will successfully run without exceptions.
But you can't invoke it (I didn't read the generated bytecode carefully, sorry) due to it's implementation:
private static final void f(Object a) {
Intrinsics.reifiedOperationMarker(4, "T"); // I didn't see
// the implementation of this line, so I thought it's
// possible to call it in other languages
Class var2 = Object.class;
System.out.println(var2);
}
Look at the comment. And look at the definition of reifiedOperationMarker
:
public static void reifiedOperationMarker(int id, String typeParameterIdentifier) {
throwUndefinedForReified();
}
And it will throw an UnsupportedOperationException
.
Conclusion: reified
can only be used in Kotlin.
It's really difficult to say whether Kotlin or Scala is better, because Scala's has more ways to get type information at runtime.
Alexey Romanov said that Scala can but Kotlin can't:
using ClassTags in a recursive function
I think this can be solved by using functions inside functions:
inline fun <reified T> g(a: Any): Int {
var recur: ((Any) -> T)? = null
recur = { recur!!.invoke(it) as T } // use T is possible here
return recur(a)
}
Note that this is only an example that syntactically correct.
It's infinite loop and unnecessary cast, of course.
He also said:
storing them in collections and using them to call ClassTag-using functions later.
This is a true problem, because this needs noinline
lambdas, while Kotlin's reified
is based on inline.
When Kotlin inlines a generic function, it naturally substitutes the type parameters by the type it was called with. E.g. with inline fun <T> foo(x: T) = ...
foo(File("."))
becomes
val x = File(".")
// body of foo with File used everywhere T was
What reified
does is just to allow using operations in body of foo
which will only make sense after this substitution but are illegal for non-reified
type parameters, such as T::class
.
The relevant Scala feature is ClassTag
/TypeTag
, not the "sophisticated type system". Effectively, it automates passing the Class<T>
(or TypeToken<T>
) as an argument, which can be done manually in Java and often is. Note that this is a completely different approach than reified
.
I don't think there's anything which reified
does which isn't possible in Scala, but the advantage of the Kotlin approach is more natural syntax: e.g. in in Scala you can't just write classOf[T]
in a ClassTag
-using method like you would classOf[File]
.
OTOH, Scala allows things which aren't possible with reified
, e.g.:
using ClassTag
s in a recursive function
storing them in collections and using them to call ClassTag
-using functions later.
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