Kotlin IllegalAccessError with += and -= for delegated Interface

时间:2017-12-08 15:39:22

标签: kotlin

I've defined this class:

class NeverNullMap<K, V>(private val backing: MutableMap<K, V> = mutableMapOf(), val default: () -> V): MutableMap<K, V> by backing {
    override operator fun get(key: K): V = backing.getOrPut(key, default)
}

And I can use it perfectly fine like this:

fun main(args: Array<String>) {
    val myMap = NeverNullMap<String, Int> {0}
    println(myMap["test"])
    myMap["test"] = myMap["test"] + 10
    println(myMap["test"])
}

as expected the output is:

0
10

But when I try to change it to:

fun main(args: Array<String>) {
    val myMap = NeverNullMap<String, Int> {0}
    println(myMap["test"])
    myMap["test"] += 10
    println(myMap["test"])
}

I get:

Exception in thread "main" java.lang.IllegalAccessError: tried to access method kotlin.collections.MapsKt__MapsKt.set(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V from class Day08Kt
    at Day08Kt.main(Day08.kt:10)

Why is this happening?

Edit:

Digging a bit into decompiled code both get compiled to completly diffrent code.

In the working version without the += it gets compiled to:

  Map var2 = (Map)myMap;
  String var3 = "test";
  Integer var4 = ((Number)myMap.get("test")).intValue() + 10;
  var2.put(var3, var4);

The non working version gets compiled to:

MapsKt.set(myMap, "test", ((Number)myMap.get("test")).intValue() + 10);

So it calles this function: https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/Maps.kt#L175

I still have no idea why that produces the Error, just why the first version behaves diffrently.

Edit: YouTrack link to the report

1 个答案:

答案 0 :(得分:4)

编辑:是的,这是一个错误,它已与KT-14227合并:

  

MutableMap.setplusAssign运算符

一起使用时,会生成错误的代码

在编译(或反编译,在本例中)后,MapsKt.set变为private方法:

@InlineOnly
private static final void set(@NotNull Map $receiver, Object key, Object value) {
    Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
    $receiver.put(key, value);
}

这解释了IllegalAccessError

现在,关于为什么它是私人的,我只是在猜测,但我觉得可能是由于这个原因:

@usbpc102指出@InlineOnly is indeed the reason for the method being private

@InlineOnly指定永远不应该直接调用该方法:

  

指定不应在没有内联

的情况下直接调用此函数

所以我觉得这种情况是set 的调用应该内联,但事实并非如此。

如果内联调用,您最终会得到与工作版本几乎完全相同的编译代码,因为该方法只包含对put的调用。

我怀疑这是由于编译器错误。