23

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.

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
rshepherd
  • 1,396
  • 3
  • 12
  • 21

5 Answers5

27

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.

Matthew Farwell
  • 60,889
  • 18
  • 128
  • 171
  • You 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. – rshepherd Sep 28 '11 at 22:05
  • 1
    The implicit conversion is on `JavaConversions`, but not on `JavaConverters`. That uses `asScala`. Many people, myself included, prefer to make this conversion explicit to avoid subtle bugs. – Daniel C. Sobral Oct 01 '11 at 22:59
  • @Daniel I guess making stuff explicit is a good idea, thanks for the tip. – Matthew Farwell Oct 01 '11 at 23:05
11

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

Didier Dupont
  • 29,398
  • 7
  • 71
  • 90
  • 1
    Thanks 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. – rshepherd Sep 28 '11 at 22:07
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.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
2

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>
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
0

As of scala 2.12.8 one could use

import scala.collection.JavaConverters._

asScalaIterator(java.util.Iterator variable).toSeq
Andriy
  • 249
  • 3
  • 8