tipos - Cómo convertir una colección scala anidada en una colección Java anidada
que son las colecciones de java (5)
Escribí esta función de propósito general, que funciona bien para mis necesidades.
def toJava(x: Any): Any = {
import scala.collection.JavaConverters._
x match {
case y: scala.collection.MapLike[_, _, _] =>
y.map { case (d, v) => toJava(d) -> toJava(v) } asJava
case y: scala.collection.SetLike[_,_] =>
y map { item: Any => toJava(item) } asJava
case y: Iterable[_] =>
y.map { item: Any => toJava(item) } asJava
case y: Iterator[_] =>
toJava(y.toIterable)
case _ =>
x
}
}
Tengo problemas de compilación entre Scala y Java.
Mi código de Java necesita un
java.util.Map<Double, java.lang.Iterable<Foo>>
Mi código de Scala tiene una
Map[Double, Vector[Foo]]
Me sale el error de compilación:
error: type mismatch;
found : scala.collection.immutable.Map[scala.Double,Vector[Foo]
required: java.util.Map[java.lang.Double,java.lang.Iterable[Foo]]
Parece que scala.collection.JavaConversions no se aplica a colecciones anidadas, a pesar de que un Vector se puede convertir implícitamente en un Iterable. Aparte de recorrer la colección de scala y hacer la conversión a mano, ¿hay algo que pueda hacer para que los tipos funcionen?
Esto se adapta mejor a mis necesidades:
def toJava(m: Any): Any = {
import java.util
import scala.collection.JavaConverters._
m match {
case sm: Map[_, _] => sm.map(kv => (kv._1, toJava(kv._2))).asJava
case sl: Iterable[_] => new util.ArrayList(sl.map( toJava ).asJava.asInstanceOf[util.Collection[_]])
case _ => m
}
}
Prueba esto si alguien busca solución en spark-scala,
importar org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema
Aquí, y está anidado WrapperArray
y match {
case x : WrappedArray[x] =>
(x.map(f => f match {case z: GenericRowWithSchema => z.mkString(",").toString()
case z:Any => z })).asJavaCollection
case _ => row.get(i).asInstanceOf[Object]
}
El código anterior hace dos cosas: 1) Si la matriz de envoltura tiene un tipo de datos primitivo, la condición case_ obtiene 2) Si la matriz de wrapper tiene un tipo de datos complejo (por ejemplo, struts), se ejecuta el caso GenericRowWithSchema.
Todas las otras soluciones son Any
to Any
, que es bastante mala para un lenguaje fuertemente tipado como Scala.
Aquí hay una solución que mantiene los tipos tanto como sea posible:
trait AsJava[T, R] {
def apply(o: T): R
}
object AsJava extends LowPriorityAsJava {
implicit class RecursiveConverter[T](o: T) {
def asJavaRecursive[R](implicit asJava: AsJava[T, R]): R = asJava(o)
}
implicit lazy val longAsJava: AsJava[Long, lang.Long] = new AsJava[Long, lang.Long] {
def apply(o: Long): lang.Long = Long.box(o)
}
implicit lazy val intAsJava: AsJava[Int, lang.Integer] = new AsJava[Int, lang.Integer] {
def apply(o: Int): lang.Integer = Int.box(o)
}
implicit lazy val doubleAsJava: AsJava[Double, lang.Double] = new AsJava[Double, lang.Double] {
def apply(o: Double): lang.Double = Double.box(o)
}
implicit def mapAsJava[K, V, KR, VR](
implicit
keyAsJava: AsJava[K, KR],
valueAsJava: AsJava[V, VR]
): AsJava[Map[K, V], util.Map[KR, VR]] =
new AsJava[Map[K, V], util.Map[KR, VR]] {
def apply(map: Map[K, V]): util.Map[KR, VR] =
map.map { case (k, v) => (keyAsJava(k), valueAsJava(v)) }.asJava
}
implicit def seqAsJava[V, VR](implicit valueAsJava: AsJava[V, VR]): AsJava[Seq[V], util.List[VR]] =
new AsJava[Seq[V], util.List[VR]] {
def apply(seq: Seq[V]): util.List[VR] = seq.map(valueAsJava(_)).asJava
}
implicit def setAsJava[V, VR](implicit valueAsJava: AsJava[V, VR]): AsJava[Set[V], util.Set[VR]] =
new AsJava[Set[V], util.Set[VR]] {
def apply(set: Set[V]): util.Set[VR] = set.map(valueAsJava(_)).asJava
}
implicit lazy val anyAsJava: AsJava[Any, AnyRef] = new AsJava[Any, AnyRef] {
def apply(o: Any): AnyRef = o match {
case x: Map[Any, Any] => mapAsJava(anyAsJava, anyAsJava)(x)
case x: Seq[Any] => seqAsJava(anyAsJava)(x)
case x: Set[Any] => setAsJava(anyAsJava)(x)
case x: Long => longAsJava(x)
case x: Int => intAsJava(x)
case x: Double => doubleAsJava(x)
case x => x.asInstanceOf[AnyRef]
}
}
}
trait LowPriorityAsJava {
implicit def otherAsJava[T]: AsJava[T, T] = new AsJava[T, T] {
def apply(o: T): T = o
}
}
Uso:
Seq(Seq.empty[Int]).asJavaRecursive
scala.collection.JavaConversions
debe estar en desuso IMHO. Es mejor que sea explícito sobre dónde y cuándo ocurre la conversión utilizando scala.collection.JavaConverters
. En tu caso:
import scala.collection.JavaConverters._
type Foo = Int // Just to make it compile
val scalaMap = Map(1.0 -> Vector(1, 2)) // As an example
val javaMap = scalaMap.map {
case (d, v) => d -> v.toIterable.asJava
}.asJava