如何在 Kotlin 中将 scala.collection.mutable.Map 转换为 scala.collection.immutable.Map?

How to convert scala.collection.mutable.Map to scala.collection.immutable.Map in Kotlin?

我在 Kotlin 代码中使用的 Scala 库提供了一个函数,该函数需要 scala.collection.immutable.Map<String!, String!>! 作为参数。

为了有一个最小的例子,我们称这个函数为f。我想将给定 Kotlin 的值映射到它,但我正在努力进行转换:

fun f(x: scala.collection.immutable.Map<String, String>) {}

fun main() {
    val m = mapOf("foo" to "bar")
    val m2 = scala.jdk.CollectionConverters.MapHasAsScala(m).asScala()
    f(m2)
}

错误:

Type mismatch.
Required:
scala.collection.immutable.Map<String, String>
Found:
scala.collection.mutable.Map<String!, String!>!

我明白,I should use .toMap,但我不能在不提供 ev 参数的情况下调用此函数:

val m2 = scala.jdk.CollectionConverters.MapHasAsScala(m).asScala().toMap<String, String>()

错误:

No value passed for parameter 'ev'

我错过了什么?

使用 scala.collection.immutable.Map.from 找到解决方案并将其放入扩展函数中。 :)

fun <A, B> Map<A, B>.toImmutableScalaMap(): scala.collection.immutable.Map<A, B> =
    scala.collection.immutable.Map.from(scala.jdk.CollectionConverters.MapHasAsScala(this).asScala())

fun main() {
    val m = mapOf("foo" to "bar")
    f(m.toImmutableScalaMap())
}

首先,ev参数是一个隐含的证据,表明可迭代对象中的泛型类型“即A”是(String, String)的子类型。这可以由 scala 中的编译器计算出来,无需为其传递参数(它是隐式的)。所以在 Scala 中这是可行的:

val myMap = ... //of type scala.collection.mutable.Map[String, String]
val immutableMap = myMap.toMap // scala.collection.immutable.Map[String, String]

但在 Kotlin 或 Java 中使用它需要传递类型 <:<[(String, String), (String, String)] 的实例(意思是 2 个字符串的元组,是 2 个字符串的元组的子类型,这实际上是正确的)。我不认为你可以想这样做,你也不能,因为非法继承。我有 2 个建议。

第一: 创建一个 Scala 对象,它可以轻松地为您完成此操作

object MapConvertor {
  def mutableToImmutable[K, V](mutableMap: scala.collection.mutable.Map[K, V]) = mutableMap.toMap
}

只需在您的 Kotlin 文件中调用它(如 MapConvertor.mutableToImmutable(...)),它们是二进制兼容的,不会有任何问题。

第二 Scala 中的 Map,也混合了 IterableOnce 特性(实现 IterableOnce 接口,这更 Java-ish :D)。所以你可以将它转换为超类型。 Map 还有一个静态方法可以从 IterableOnce 创建映射。所以:

scala.collection.IterableOnce<scala.Tuple2<String, String>> immutableMapIterable;
immutableMapIterable = (scala.collection.IterableOnce<scala.Tuple2<String, String>>) Test.m();
return scala.collection.immutable.Map.from(immutableMapIterable);