Я определил этот класс:
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) }
И я могу использовать его совершенно прекрасно:
fun main(args: Array<String>) { val myMap = NeverNullMap<String, Int> {0} println(myMap["test"]) myMap["test"] = myMap["test"] + 10 println(myMap["test"]) }
как и ожидалось, выход:
0 10
Но когда я пытаюсь изменить его на:
fun main(args: Array<String>) { val myMap = NeverNullMap<String, Int> {0} println(myMap["test"]) myMap["test"] += 10 println(myMap["test"]) }
Я получил:
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)
Почему это происходит?
Редактировать:
Копая бит в декомпилированный код, оба скомпилируются до совершенно разного кода.
В рабочей версии без +=
она компилируется в:
Map var2 = (Map)myMap; String var3 = "test"; Integer var4 = ((Number)myMap.get("test")).intValue() + 10; var2.put(var3, var4);
Нерабочая версия скомпилирована для:
MapsKt.set(myMap, "test", ((Number)myMap.get("test")).intValue() + 10);
Поэтому он вызывает эту функцию: https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/Maps.kt#L175
Я до сих пор не знаю, почему это вызывает ошибку, просто почему первая версия ведет себя по-разному.
Изменить: ссылка YouTrack на отчет
Изменить: да, это ошибка, она была объединена с KT-14227 :
Неправильный код генерируется при использовании
MutableMap.set
сplusAssign
оператораplusAssign
После компиляции (или декомпиляции в этом случае) 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
действительно является причиной того, что метод является private
.
@InlineOnly
указывает, что метод никогда не должен вызываться напрямую:
Указывает, что эту функцию нельзя вызывать напрямую без вставки
поэтому я чувствую, что это случай, когда вызов set
должен быть встроен, но это не так.
Если бы вызов был встроен, вы бы закончили с компилированным кодом, который практически идентичен рабочей версии, так как метод содержит только вызов.
Я подозреваю, что это связано с ошибкой компилятора.