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.
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.
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
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.
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>
As of scala 2.12.8 one could use
import scala.collection.JavaConverters._
asScalaIterator(java.util.Iterator variable).toSeq
Related
I often occur the problem that although I imported the scala.collection.JavaConverters._ converters into my scala file, the implicit conversion does not happen.
Assume the following code (where submissionStorage.get is some Java function returning a Java Map)
import scala.collection.JavaConverters._
import scala.collection.mutable
...
val submissions: java.util.Map[String, String] = submissionStorage.get(formname)
val submissionJavaKeys: java.util.Set[String] = submissions.keySet()
val submissionScalaKeys: mutable.Set[String] = submissionJavaKeys
Leds into the following compile error:
type mismatch; found : java.util.Set[String] required:
scala.collection.mutable.Set[String]
Now the documentation of the JavaConverters states that there is a conversion "scala.collection.mutable.Set <=> java.util.Set", so exactlly what I need here.
What am I doing wrong, so under which conditions do the JavaConverters work?
JavaConverters adds asJava and asScala methods. You want JavaConversions instead.
Converting from java.util.ArrayList to scala.collection.immutable.List, the 2.10 compiler and run-time may seem to be in disagreement, about the type of val emits:
import org.ahocorasick.trie._
import scala.collection.JavaConverters._ // convert Java colllections to Scala ones
object wierd {
val trie = new Trie
def trieInit(patterns: List[String]) {
trie.onlyWholeWords();
for (pattern <- patterns)
trie.addKeyword(pattern)
}
def patternTest(text : String) : List[String] =
{
val emitsJ = trie.parseText(text)
val emits = emitsJ.asScala map (i => i.getKeyword)
println(s"converted from ${emitsJ.getClass} to ${emits.getClass}")
//return(emits)
return (List.empty[String])
}
trieInit(List("hello"))
patternTest("hello")
}
Yields:
converted from class java.util.ArrayList to class scala.collection.immutable.$colon$colon
Now changing to return the real value by changing only the return line -
import org.ahocorasick.trie._
import scala.collection.JavaConverters._ // convert Java colllections to Scala ones
object wierd {
val trie = new Trie
def trieInit(patterns: List[String]) {
trie.onlyWholeWords();
for (pattern <- patterns)
trie.addKeyword(pattern)
}
def patternTest(text : String) : List[String] =
{
val emitsJ = trie.parseText(text)
val emits = emitsJ.asScala map (i => i.getKeyword)
println(s"converted from ${emitsJ.getClass} to ${emits.getClass}")
return(emits)
//return (List.empty[String])
}
trieInit(List("hello"))
patternTest("hello")
}
Yields a compilation error:
[error] reproduce.scala:23: type mismatch;
[error] found : Iterable[String]
[error] required: List[String]
[error] return(emits)
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
What would be the simple explanation for this?
How would I better approach the conversion?
JavaConverters convert to the Scala collection closest to the Java collection that it has pimped. You still have to call toList to further transform it to the collection you want:
val emits = emitsJ.asScala.toList map (i => i.getKeyword)
See related: What is the difference between JavaConverters and JavaConversions in Scala?
So what's happening here is that the underlying object returned to you from asScala is a List but it's been downcast to an Iterable since List <: Iterable this is all fine until you want to use the Iterable as a list. The easiest option is to call toList on it.
For a little more detail you can browse the source:
case class JCollectionWrapper[A](underlying: ju.Collection[A]) extends AbstractIterable[A] with Iterable[A] {
def iterator = underlying.iterator
override def size = underlying.size
override def isEmpty = underlying.isEmpty
def newBuilder[B] = new mutable.ArrayBuffer[B]
}
So your call to asScala gives you back this wrapper. Next, your call to map is using this CanBuildFrom which is then used in this map operation, which finally gives you the result which happens to be a List because the builder is a ListBuffer and the result is a List, which again happens to be an Iterable because the can build from pattern downcasts to Iterable. Hope that explains everything :)
the return type of trie.parseText is declared as java.util.Collection[Emit]. This isn't very specific, there are lots of possible subtypes of Collection, they aren't specifying what specific type they are going to return, it could be a TreeSet, it could be a Vector, it could be an ArrayList. But as far as the compiler is concerned, it could be anything which is a sub-type of Collection. You have inspected it at run-time and seen that for some particular input, it happened to return an ArrayList, but there is no way the compiler could know this.
When you call .asScala on this, you are using this implicit definition from JavaConverters
implicit def iterableAsScalaIterableConverter[A](i: java.lang.Iterable[A]): convert.Decorators.AsScala[Iterable[A]]
Which converts a java.lang.Itaerable into a scala.collection.Iterable.
There is no converter for Collection so you get a converter for the next most specific thing, Iterable. you call map on this Iterable and get an Iterable back.
Now, just like you have inspected the runtime value of the Collection returned from parseText and seen that it was a ArrayList you have inspected the value returned from this map operation and have seen that it is a scala.collection.immutable.List, but again, the compiler can't know this, all that it can know is that you gotten something which is a subclass of Iterable back.
Simply calling .toList on the resulting Iterable should be all you need to do.
I use Scala 2.10.3.
I have result.iterator() returning Iterator<java.util.Map<String, Object>>. (in Java so)
I want to convert it to the Scala equivalent.
I use import scala.collection.JavaConversions._ to try to do the trick.
However it seems that it is unable to take into account type parameters, in this case, it can convert java.util.Iterator to the Scala equivalent, but fails to convert java.util.Map in the Scala equivalent.
Indeed, a compiler error occurs at this line:
val results: Iterator[Map[String, AnyRef]] = result.iterator()
type mismatch;
found : java.util.Iterator[java.util.Map[String,Object]]
required: scala.collection.Iterator[scala.collection.immutable.Map[String,AnyRef]]
val results: Iterator[Map[String, AnyRef]] = result.iterator()
^
Is there a short way to do the trick?
You could explicitly specify what you want to convert using JavaConverters instead of JavaConversions like this:
import scala.collection.JavaConverters._
def javaIt: java.util.Iterator[java.util.Map[String, Object]] = ???
def scalaIt = javaIt.asScala map {_.asScala}
// Iterator[scala.collection.mutable.Map[String,Object]]
I have java API which return this type:
ArrayList[ArrayList[String]] = Foo.someJavaMethod()
In scala program, I need to send above type as a parameter to a scala function 'bar' whose type is
def bar(param: List[List[String]]) : List[String] = {
}
so I call bar like:
val list = bar(Foo.someJavaMethod())
but this does not work as I get compile error.
I thought have this import
import scala.collection.JavaConversions._
will do implicit automatic conversion between Java and Scala collections.
I also tried using like:
Foo.someJavaMethod().toList
but that does not work either.
What is the solution to this problem?
First, ArrayList does not convert to List, it converts to a Scala Buffer. Second, implicit conversion will not recurse into the elements of your collections.
You'll have to manually map the inner lists. Either with implicit conversions:
import collection.JavaConversions._
val f = Foo.someJavaMethod()
bar(f.toList.map(_.toList))
Or, more explicitly, if you prefer:
import collection.JavaConverters._
val f = Foo.someJavaMethod()
bar(f.asScala.toList.map(_.asScala.toList))
How can I convert a java 1.4 Collection to a Scala Seq?
I am trying to pass a java-collection to a scala method:
import scala.collection.JavaConversions._
// list is a 1.4 java.util.ArrayList
// repository.getDir is Java legacy code
val list = repository.getDir(...)
perform(list)
def perform(entries: List[SVNDirEntry]) = ...
I always receive this error:
type mismatch; found : java.util.Collection[?0] where type ?0 required: List
[SVNDirEntry]
So I guess I have to create the parameterized Sequence myself as Scala is only able to create an unparameterized Iterable?
First you have to make sure that list has the type java.util.List[SVNDirEntry]. To do this, use a cast:
list.asInstanceOf[java.util.List[SVNDirEntry]]
After that, the implicit conversion will be resolved for you if you import the JavaConversions object. An implicit conversion to a Scala sequence exists in the JavaConversions object. See the following example with a list of strings being passed to a method that expects a Scala sequence:
scala> val jvlist: java.util.List[_] = new java.util.ArrayList[String]
jvlist: java.util.List[_] = []
scala> jvlist.asInstanceOf[java.util.List[String]]
res0: java.util.List[String] = []
scala> import collection.JavaConversions._
import collection.JavaConversions._
scala> def perform(scalaseq: Seq[String]) = println("ok")
perform: (scalaseq: scala.collection.Seq[String])Unit
scala> perform(res0)
ok
These conversions do not copy the elements - they simply construct a wrapper around a Java collection. Both versions point to the same underlying data. Thus, there is no implicit conversion in JavaConversions to immutable Scala lists from mutable Java lists, because that would enable changing the contents of a Scala collection that is guaranteed to be immutable.
In short - prefer Seq[...] to List[...] when defining parameters for methods if you can live with a less specific interface (as in perform above). Or write your own function that does the conversion by copying the elements.
You have to cast the legacy collection down to the target type. Something along the lines of:
perform(list.asInstanceOf[List[SVNDirEntry]])