java.util.List
, but you want to use the awesome methods contained in Scala's scala.List
(soon to be scala.collection.immutable.List
). I'll show you how to do what you want in a second, but first, and importantly, I need to explain that what you actually want to do is NOT convert a Java
List
to a Scala List
. Why? It's because, despite the name similarity, they're actually very different things...The difference is that Java's
List
is an interface, while Scala's List
is an implementation. The List
interface in Java represents a concept: a collection of elements that maintains insertion order (i.e. a sequence) and allows, among other things, random access to its elements. The most commonly used implementation is ArrayList, which is mutable (i.e. invoking add(element) changes the existing list) and has constant-time random access. Unlike Java's List
interface, Scala's List
is a specific implementation of a sequence. In that sense, it is a parallel to ArrayList
, but in many other senses, it's nothing like it. The List
class imported by default in Scala is an immutable linked list - it can't be changed after it's been created and operations requiring random access take linear time. Scala's List
is not a direct substitute for Java's List
and it's extremely different to ArrayList
as well. So, what to do?Most people want to convert their Java
List
to a Scala List
just so they can use cool Scala functions like map()
, filter()
, find()
, foreach()
, foldLeft()
and flatMap()
. And that's a perfectly good excuse. But if you look into the Scala API, you'll see that all of these useful methods are available on any class that implements the Iterable
trait. (From Scala 2.8, they are also available on the Traversable
trait, which the Iterable
trait extends.)If we look a bit further into the Scala API, we'll see that there's also a trait called
Seq
, which is a slightly better parallel to Java's List
than the Iterable
or Traversable
traits. So what you really want to end up with when you bring your Java List
into your Scala code is a Scala Seq
, not a Scala List
.That's enough jibber-jabber. I'm sure you've learnt your lesson.
Here's a great way to convert a Java
List
to a Scala Seq
in Scala 2.7:
scala> var javaList = new java.util.ArrayList[Int]()
javaList: java.util.ArrayList[Int] = []
scala> javaList.add(3)
res0: Boolean = true
scala> javaList.add(4)
res1: Boolean = true
scala> javaList.add(5)
res2: Boolean = true
scala> val scalaSeq = new collection.jcl.BufferWrapper[Int]() {
def underlying = javaList
}
scalaSeq: java.lang.Object with scala.collection.jcl.BufferWrapper[Int]{def underlying: java.util.ArrayList[Int]} = [3, 4, 5]
scala> println(javaList)
[3, 4, 5]
scala> println(scalaSeq)
[3, 4, 5]
In Scala 2.8, the EPFL LAMP guys have realised the importance of this kind of conversion and gone one better by providing a class full of conversions that do exactly what we want, like this:
scala> var javaList = new java.util.ArrayList[Int]()
javaList: java.util.ArrayList[Int] = []
scala> javaList.add(3)
res0: Boolean = true
scala> javaList.add(4)
res1: Boolean = true
scala> javaList.add(5)
res2: Boolean = true
scala> val scalaSeq = scala.collection.JavaConversions.asBuffer(javaList)
scalaSeq: scala.collection.mutable.Buffer[Int] = Buffer(3, 4, 5)
scala> println(javaList)
[3, 4, 5]
scala> println(scalaSeq)
Buffer(3, 4, 5)
Of course, so much of the appeal of Scala is attributable to having to write less code, so it would be nice to not even have to invoke these operations within functions but instead to have implicit functions that will perform the conversions whenever they're necessary.
In Scala 2.7, the implicit conversion can be defined like this:
implicit def javaList2Seq[T](javaList: java.util.List[T]) : Seq[T] =
new BufferWrapper[T]() { def underlying = javaList }
And in Scala 2.8, all you need to do is import the existing implicit functions!
import scala.collection.JavaConversions._
That was a very good post. Very relevant to my own exploration of Scala right now.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThank you for the post. It is very useful to know about the implicit conversions found in "import scala.collection.JavaConversions._" if you are mixing both scala and java
ReplyDelete