java.util.Iterator to Scala list?
Solution 1
That would work if obj.getKeys() was a java.util.Iterator<String>
. I suppose it is not.
If obj.getKeys()
is just java.util.Iterator
in raw form, not java.util.Iterator<String>
, not even java.util.Iterator<?>
, this is something scala tend to dislikes, but anyway, there is no way scala will type your expression as List[String]
if it has no guarantee obj.getKeys()
contains String.
If you know your iterator is on Strings, but the type does not say so, you may cast :
obj.getKeys().asInstanceOf[java.util.Iterator[String]]
(then go on with .asScala.toList
)
Note that, just as in java and because of type erasure, that cast will not be checked (you will get a warning). If you want to check immediately that you have Strings, you may rather do
obj.getKeys().map(_.asInstanceOf[String])
which will check the type of each element while you iterate to build the list
Solution 2
You don't need to call asScala, it is an implicit conversion:
import scala.collection.JavaConversions._
val javaList = new java.util.LinkedList[String]() // as an example
val scalaList = javaList.iterator.toList
If you really don't have the type parameter of the iterator, just cast it to the correct type:
javaList.iterator.asInstanceOf[java.util.Iterator[String]].toList
EDIT: Some people prefer not to use the implicit conversions in JavaConversions, but use the asScala/asJava decorators in JavaConverters to make the conversions more explicit.
Solution 3
I dislike the other answers. Hell, I dislike anything that suggests using asInstanceOf
unless there's no alternative. In this case, there is. If you do this:
private lazy val keys : List[String] = obj.getKeys().asScala.collect {
case s: String => s
}.toList
You turn the Iterator[_]
into a Iterator[String]
safely and efficiently.
Solution 4
Note that starting Scala 2.13
, package scala.jdk.CollectionConverters
replaces deprecated packages scala.collection.JavaConverters/JavaConversions
when it comes to implicit conversions between Java and Scala collections:
import scala.jdk.CollectionConverters._
// val javaIterator: java.util.Iterator[String] = java.util.Arrays.asList("a", "b").iterator
javaIterator.asScala
// Iterator[String] = <iterator>
Martin Smith
Updated on June 09, 2020Comments
-
Martin Smith almost 4 years
I have the following code:
private lazy val keys: List[String] = obj.getKeys().asScala.toList
obj.getKeys
returns ajava.util.Iterator<java.lang.String>
Calling
asScala
, viaJavaConverers
(which is imported) according to the docs..java.util.Iterator <==> scala.collection.Iterator
scala.collection.Iterator
definesdef toList: List[A]
So based on this I believed this should work, however here is the compilation error:
[scalac] <file>.scala:11: error: type mismatch; [scalac] found : List[?0] where type ?0 [scalac] required: List[String] [scalac] private lazy val keys : List[String] = obj.getKeys().asScala.toList [scalac] one error found
I understand the type parameter or the java Iterator is a Java String, and that I am trying to create a list of Scala strings, but (perhaps naively) thought that there would be an implicit conversion.
-
Martin Smith over 12 yearsYou are right, there is no type parameter for the Iterator that is being returned from the getKeys() method. Very astute observation, thank you. Though it appears I do need the asScala in there, in that case. Perhaps there is something else I am missing.
-
Martin Smith over 12 yearsThanks very much for the answer! seems like you and Matthew Farwell agree for the most part. I did what you suggest, obj.getKeys().asInstanceOf[java.util.Iterator[String]].asScala.toList and that seems to work, and I did not get a compilation warning.
-
Daniel C. Sobral over 12 yearsThe implicit conversion is on
JavaConversions
, but not onJavaConverters
. That usesasScala
. Many people, myself included, prefer to make this conversion explicit to avoid subtle bugs. -
Matthew Farwell over 12 years@Daniel I guess making stuff explicit is a good idea, thanks for the tip.