can anyone explain the reified keyword to me? What...
# announcements
k
can anyone explain the reified keyword to me? What I'm reading online isn't making a whole lot of sense and I need it to click
g
Generics in Kotlin are non-reified (same as in JVM), it means that they are erased after compilation, so you don’t have access to real generic class:
Copy code
fun <T> foo() {
        println(T::class) //Will not compile
}
But Kotlin supports also reified generics (but only for inline functions), it means that compiler will compile this code in different way and allow you access to real generic type class:
Copy code
inline fun <reified T> foo() {
        println(T::class) //Works fine
}
👍 3
It allow you to write code with nice API, that don’t require to pass class instance as additional param, but get it from generic
a
I have a function:
Copy code
inline fun <reified T> View.findParentOfType(): T? {
    var p = parent
    while (p != null && p !is T) p = p.parent
    return p as T?
}
which lets me call for example:
Copy code
email.findParentOfType<TextInputLayout>()?.error ?: email.error
The function gets decompiled to:
Copy code
private static final Object findParentOfType(@NotNull View $receiver) {
  ViewParent p;
  for(p = $receiver.getParent(); p != null; p = p.getParent()) {
     Intrinsics.reifiedOperationMarker(3, "T");
     if(p instanceof Object) {
        break;
     }
  }

  Intrinsics.reifiedOperationMarker(1, "T?");
  return (Object)p;
}
Which leaves the question of how it works open to me, honestly. What’s up with the
if(p instanceof Object) break;
? 😄
g
It can be a problem of decompilation, always check bytecode instead.
k
that makes much more sense! Thank you
a
Hmm, bytecode shows the same thing. Here’s a simpler example:
Copy code
inline fun <reified T> View.parentIsInstance() = parent is T
shows this bytecode:
Copy code
// access flags 0x1A
  // signature <T:Ljava/lang/Object;>(Landroid/view/View;)Z
  // declaration: boolean parentIsInstance<T>(android.view.View)
  private final static parentIsInstance(Landroid/view/View;)Z
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    LINENUMBER 137 L0
    ALOAD 0
    INVOKEVIRTUAL android/view/View.getParent ()Landroid/view/ViewParent;
    ICONST_3
    LDC "T"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.reifiedOperationMarker (ILjava/lang/String;)V
    INSTANCEOF java/lang/Object
    IRETURN
   L1
    LOCALVARIABLE $receiver Landroid/view/View; L0 L1 0
    LOCALVARIABLE $i$f$parentIsInstance I L0 L1 1
    MAXSTACK = 3
    MAXLOCALS = 2
Decompiled to:
Copy code
private static final boolean parentIsInstance(@NotNull View $receiver) {
      ViewParent var10000 = $receiver.getParent();
      Intrinsics.reifiedOperationMarker(3, "T");
      return var10000 instanceof Object;
   }
What I was missing, I guess, was that the function gets inlined, so it would not look like that bytecode. Type parameter would be replaced with the actuall call site argument I presume.
reifiedOperationMarker
always throws an exception if ever called
The right way to look at it, was to additionally write some code that calls that function and look at its bytecode, for example:
Copy code
inline fun <reified T> View.parentIsInstance() = parent is T

fun testReified(x: View) = x.parentIsInstance<LinearLayout>()
gets decompiled to:
Copy code
private static final boolean parentIsInstance(@NotNull View $receiver) {
      ViewParent var10000 = $receiver.getParent();
      Intrinsics.reifiedOperationMarker(3, "T");
      return var10000 instanceof Object;
   }

   public static final boolean testReified(@NotNull View x) {
      Intrinsics.checkParameterIsNotNull(x, "x");
      return x.getParent() instanceof LinearLayout;
   }
which now totally makes sense.