https://kotlinlang.org logo
Title
k

kevin.cianfarini

08/24/2017, 3:20 AM
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

gildor

08/24/2017, 3:41 AM
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:
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:
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

arekolek

08/24/2017, 8:32 AM
I have a function:
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:
email.findParentOfType<TextInputLayout>()?.error ?: email.error
The function gets decompiled to:
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

gildor

08/24/2017, 8:38 AM
It can be a problem of decompilation, always check bytecode instead.
k

kevin.cianfarini

08/24/2017, 4:01 PM
that makes much more sense! Thank you
a

arekolek

08/25/2017, 9:56 AM
Hmm, bytecode shows the same thing. Here’s a simpler example:
inline fun <reified T> View.parentIsInstance() = parent is T
shows this bytecode:
// 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:
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:
inline fun <reified T> View.parentIsInstance() = parent is T

fun testReified(x: View) = x.parentIsInstance<LinearLayout>()
gets decompiled to:
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.