17

I'm having compilation issues between Scala and Java.

My Java code needs a

java.util.Map<Double, java.lang.Iterable<Foo>>

My scala code has a

Map[Double, Vector[Foo]]

I get the compilation error:

error: type mismatch;
found   : scala.collection.immutable.Map[scala.Double,Vector[Foo]
required: java.util.Map[java.lang.Double,java.lang.Iterable[Foo]]

It seems the scala.collection.JavaConversions don't apply to nested collections, even though a Vector can be implictly converted to an Iterable. Short of iterating through the scala collection and doing the conversion by hand, is there something I can do to make the types work?

5 Answers 5

7

scala.collection.JavaConversions should be deprecated IMHO. You are better off being explicit about where and when the conversion happens by using scala.collection.JavaConverters. In your case:

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
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for pointing me to JavaConversions. I didn't know about those. I also needed to do some nasty Java Generics Wildcarding for my method in order to get the return types working, i.e. converting my declared return type to public <T extends Iterable<Foo>> Map<Double,T> createMap()
6

This better suited my needs:

  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
    }
  }

Comments

4

I wrote this general purpose function, which works well for my needs.

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
  }
}

3 Comments

Nice! I also wonder if you wrote a similar recursive toScala function..
''' The recursive toScala function I ended up writing: def toScala(x: Any): Any = { import collection.JavaConversions._ x match { case y: java.util.Map[, _] => mapAsScalaMap(y).map{ case (d, v) => toScala(d) -> toScala(v) } case y: java.lang.Iterable[] => iterableAsScalaIterable(y).toList.map { item: Any => toScala(item) } case y: java.util.Iterator[_] => toScala(y) case _ => x } } '''
Separately, I posted a related question here: stackoverflow.com/questions/43462034/…
0

Try this if anyone looking for solution in spark-scala,

import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema

Here, y is nested 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]
        }

The above code, does two things, 1) If wrapper Array has primitive data type, condition case_ gets through 2) If wrapper Array has Complex data type (say struts), case GenericRowWithSchema executes.

Comments

0

All the other solutions are Any to Any, which is pretty bad for a strongly typed language like Scala.
Here is a solution that keeps the types as much as possible:

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
  }
}

Usage:

Seq(Seq.empty[Int]).asJavaRecursive

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.