简体   繁体   中英

Why can't this code run, is it a bug in Kotlin?

I write a piece of code, then compile it with no errors. but when I run, it throws an exception (java.lang.NoClassDefFoundError: TesKt$test$1$1).

Is this a language bug?

private fun test(block:()->Unit) = arrayOf(1).map {

    object {

        fun print() {
            println("Hello print")
            block()
        }
    }
}

fun main(args: Array<String>) {
    val array = test{println("Hello main")}
    array[0].print()
}

I'm going to conclude that it's a bug in the Kotlin compiler. I played a lot with this code and looked into the compiled bytecode. It seems the compiler is losing some information about the anonymous class which is defined by object .

If we add a type to that object, then it works fine:

interface Printable {
    fun print()
}

private fun test(block:()->Unit): List<Printable> {
    return arrayOf(1).map {

        object: Printable {

            override fun print() {
                println("Hello print")
                block()
            }
        }
    }
}

fun main(args: Array<String>) {
    val array = test{println("Hello main")}
    array[0].print()
}

As you can see, I only defined a simple Printable interface and annotated the anonymous class with this interface.

Let's call your code: version A , and this code of mine: Version B .

I compiled code A and then decompiled it to Java . Here's the result:

import TestKt.test.1.1;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import kotlin.Metadata;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 15},
   bv = {1, 0, 3},
   k = 2,
   d1 = {"\u0000#\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0010 \n\u0002\b\u0002\n\u0002\u0018\u0002*\u0001\b\u001a\u0019\u0010\u0000\u001a\u00020\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003¢\u0006\u0002\u0010\u0005\u001a\u001c\u0010\u0006\u001a\b\u0012\u0004\u0012\u00020\b0\u00072\f\u0010\t\u001a\b\u0012\u0004\u0012\u00020\u00010\nH\u0002"},
   d2 = {"main", "", "args", "", "", "([Ljava/lang/String;)V", "test", "", "TestKt$test$1$1", "block", "Lkotlin/Function0;"}
)
public final class TestKt {
/*#19:*/private static final List<1> test(Function0<Unit> block) {
      Object[] $this$map$iv = new Integer[]{1};
      int $i$f$map = false;
      Collection destination$iv$iv = (Collection)(new ArrayList($this$map$iv.length));
      int $i$f$mapTo = false;
      Integer[] var6 = $this$map$iv;
      int var7 = $this$map$iv.length;

      for(int var8 = 0; var8 < var7; ++var8) {
         Object item$iv$iv = var6[var8];
         int it = ((Number)item$iv$iv).intValue();
         int var11 = false;
/*#31:*/ TestKt.test..inlined.map.lambda.1 var13 = new TestKt.test..inlined.map.lambda.1(block);
         destination$iv$iv.add(var13);
      }

      return (List)destination$iv$iv;
   }

   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      List array = test((Function0)TestKt.main.array.1.INSTANCE);
/*#41:*/((1)array.get(0)).print();
   }
}

And this is the result of the same process for code B :

import TestKt.test..inlined.map.lambda.1;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import kotlin.Metadata;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 15},
   bv = {1, 0, 3},
   k = 2,
   d1 = {"\u0000\"\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0010 \n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\u001a\u0019\u0010\u0000\u001a\u00020\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003¢\u0006\u0002\u0010\u0005\u001a\u001c\u0010\u0006\u001a\b\u0012\u0004\u0012\u00020\b0\u00072\f\u0010\t\u001a\b\u0012\u0004\u0012\u00020\u00010\nH\u0002"},
   d2 = {"main", "", "args", "", "", "([Ljava/lang/String;)V", "test", "", "LPrintable;", "block", "Lkotlin/Function0;"}
)
public final class TestKt {
/*#19:*/private static final List<Printable> test(Function0<Unit> block) {
      Object[] $this$map$iv = new Integer[]{1};
      int $i$f$map = false;
      Collection destination$iv$iv = (Collection)(new ArrayList($this$map$iv.length));
      int $i$f$mapTo = false;
      Integer[] var6 = $this$map$iv;
      int var7 = $this$map$iv.length;

      for(int var8 = 0; var8 < var7; ++var8) {
         Object item$iv$iv = var6[var8];
         int it = ((Number)item$iv$iv).intValue();
         int var11 = false;
/*#31:*/ 1 var13 = new 1(block);
         destination$iv$iv.add(var13);
      }

      return (List)destination$iv$iv;
   }

   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      List array = test((Function0)TestKt.main.array.1.INSTANCE);
/*#41:*/((Printable)array.get(0)).print();
   }
}

As you can see, the only differences are in the first line, as well as line numbers 19, 31 and 41 (commented like #19: and so on).

In code A a type (strangely) with the name of 1 is expected. But this type 1 , which is packaged as TestKt.test.1.1 , is not found and so you got your error ( NoClassDefFoundError: TesKt$test$1$1 ).

Inn code B , however, a more clear type of Printable is expected and found.

If the compiler had compiled the very first line of code A just like code B ( import TestKt.test..inlined.map.lambda.1; instead of import TestKt.test.1.1; ) then your code would have worked.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM