Не обнуляемая изменчивая карта

  • Ява: 1.8.0_102
  • Котлин: 1.0.4

Я пытаюсь создать карту, где вы можете сделать что-то вроде map["key"] += 5 похожего на javascript.

В Kotlin уже есть withDefault, который решает одну часть этого, но функция get карты по-прежнему возвращает значение, допускающее значение NULL, поэтому я приступил к своей собственной реализации, вдохновленной withDefault

interface NonNullableMutableMap<K,V> : MutableMap<K,V> {
  override fun put(key: K, value: V): V
  override fun get(key: K): V
}

fun <K,V> MutableMap<K,V>.withoutNullValues(default: () -> V): NonNullableMutableMap<K, V> {
  return NonNullableMapWrapper(this, default)
}

class NonNullableMapWrapper<K,V>(val map: MutableMap<K,V>, val default: () -> V) : NonNullableMutableMap<K,V> {
  override fun put(key: K, value: V): V = map.put(key, value) ?: default()

  override fun get(key: K): V {
      val value = map.getOrPut(key, default)
      return value
  }
  override val size: Int get() = map.size

  override fun containsKey(key: K): Boolean = map.containsKey(key)

  override fun containsValue(value: V): Boolean = map.containsValue(value)

  override fun isEmpty(): Boolean = map.isEmpty()

  override val entries: MutableSet<MutableMap.MutableEntry<K, V>> get() = map.entries
  override val keys: MutableSet<K> get() = map.keys
  override val values: MutableCollection<V> get() = map.values

  override fun clear() {
      map.clear()
  }

  override fun putAll(from: Map<out K, V>) {
    map.putAll(from)
  }

  override fun remove(key: K): V {
    return map.remove(key) ?: default()
  }
}

Я создал следующий модульный тест, чтобы проверить его

class NonNullableMapTest {

  @Test
  fun notNullableTest() {
      val map = HashMap<String, Long>().withoutNullValues { 0 }
      map["first"] += 10L
      map["second"] -= 10L
      assertThat(map["first"]).isEqualTo(10L)
      assertThat(map["second"]).isEqualTo(-10L)
      assertThat(map["third"]).isEqualTo(0L)
  }
}

Но при запуске теста я получаю следующую ошибку:

tried to access method kotlin.collections.MapsKt__MapsJVMKt.set(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V from class foo.bar.NonNullableMapTest
java.lang.IllegalAccessError: tried to access method kotlin.collections.MapsKt__MapsJVMKt.set(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V from class foo.bar.NonNullableMapTest

Любая идея, как решить эту проблему?


person martin treurnicht    schedule 12.10.2016    source источник
comment
В настоящее время withDefault предназначен для использования только в сценарии делегирования свойства, он не влияет на возвращаемое значение функции get. См. KT-11851.   -  person Ilya    schedule 13.10.2016


Ответы (2)


Это похоже на ошибку для меня. Я рекомендую сообщить об этом по адресу Kotlin (KT) | ЮТрек.

Один из способов обойти это — явно определить set в интерфейсе NonNullableMutableMap. например.:

interface NonNullableMutableMap<K, V> : MutableMap<K, V> {
    override fun put(key: K, value: V): V
    override fun get(key: K): V
    operator fun set(key: K, value: V) {
        put(key, value)
    }
}
person mfulton26    schedule 12.10.2016
comment
Спасибо! это решает проблему, с которой я столкнулся, я сообщу об этом команде Kotlin - person martin treurnicht; 13.10.2016
comment
Я упростил это до основ, вот суть github для всех, кто заинтересован в его использовании com/martintreurnicht/ - person martin treurnicht; 13.10.2016
comment
Хорошее решение по сути. Я бы возражал против getOrPut только потому, что у него есть неожиданный побочный эффект: он просто записывает обратно в карту, что делает get неидемпотентным. Другими словами, если вы вызовете containsKey до и после getOrPut, вы получите разные результаты. То же самое касается size() и большого набора других методов. И это ожидаемо. Но это не ожидается с get, который, я думаю, должен действовать идемпотентно. В моей реализации я использую getOrDefault. - person Kostas Filios; 15.08.2017

Что касается ошибки времени выполнения, которую вы получаете, в настоящее время существует ошибка в том, как оператор += компилируется для встроенной функции расширения MutableMap.set: https://youtrack.jetbrains.com/issue/KT-14227

Обходной путь - не использовать +=:

map["first"] = map["first"] + 10L
person Ilya    schedule 12.10.2016