问题
I have the following code:
private lazy val keys: List[String] = obj.getKeys().asScala.toList
obj.getKeys
returns a java.util.Iterator<java.lang.String>
Calling asScala
, via JavaConverers
(which is imported) according to the docs..
java.util.Iterator <==> scala.collection.Iterator
scala.collection.Iterator
defines
def 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.
回答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
回答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.
回答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.
回答4:
As of scala 2.12.8 one could use
import scala.collection.JavaConverters._
asScalaIterator(java.util.Iterator variable).toSeq
回答5:
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>
来源:https://stackoverflow.com/questions/7589561/java-util-iterator-to-scala-list