如何在 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);
我在 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);