I am thoroughly confused by this behavior in the Scala REPL:
scala> import java.util.Map
import java.util.Map
scala> import java.util.HashMap
import java.util.HashMap
scala> val jMap:java.util.Map[String,Int]=new HashMap[String,Int]("first"->1,"second" -> 2)
<console>:12: error: type mismatch;
found : (String, Int)
required: Float
val jMap =new HashMap[String,Int]("first"->1,"second" -> 2)
^
<console>:12: error: type mismatch;
found : (String, Int)
required: Float
val jMap=new HashMap[String,Int]("first"->1,"second" -> 2)
^
Can someone help explain what's going on here ?
java.util.HashMap does not provide capability to construct the map by passing a vararg of (K, V), but there is a two-arg constructor accepting initialCapacity: Int and loadFactor: Float, that's why you're getting the compile error about a Float being required.
(updated for scala 2.13+) The idiomatic approach in Scala would be to just use Scala immutable maps (no imports required):
val map = Map("first" -> 1, "second" -> 2).asJava
If your Scala code works with a Java library that returns a java.util.Map, you can convert it into a Scala map explicitly by using scala.jdk.CollectionConverters like so:
import scala.jdk.CollectionConverters._
val javaMap: java.util.Map[String, String] = // ...
val map = javaMap.asScala
// or vice versa if you need to pass it into the Java API
val javaMap = Map("first" -> 1, "second" -> 2).asJava
Note that asScala is just a wrapper to the underlying Java map (so if that one is mutable, the wrapped map will also be mutable) and is an instance of scala.collection.Map. In order to be fully idiomatic and benefit from Scala's immutability guarantees, you might need to add another .toMap at the end that will convert it to scala.collection.immutable.Map (which is the default Map).
If you use scala 2.12 or older, instead of scala.jdk.CollectionConverters._ import scala.collection.JavaConverters._.
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.
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]]
While trying to make a map in Scala, I receive the following error message: object Map is not a value
The code I'm using is the following:
val myStringMap = Map[String, String]("first" -> "A", "second" -> "B", "third" -> "C")
I am quite puzzled as to why I cannot create this map because after looking at Scala documentation, it seems to me that the syntax of my code is proper.
When you see the error "object is not a value" this typically means that the in-scope type is a Java type - you probably are importing java.util.Map in scope
scala> Map(1 -> "one")
res0: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one)
But
scala> import java.util.Map
import java.util.Map
scala> Map(1 -> "one")
<console>:9: error: object Map is not a value
Map(1 -> "one")
^
Remember, in scala each class comes with a (optional) companion object which is a value. This is not true of Java classes.
Because in scala each native scala class comes with an (optional) companion object (allowing for assignment from the companion object as in your example code) when incorporating a java class into scala code always remember to instantiate the class by invoking the constructor, ie. use keyword "new", thus creating a value.
Just found this so maybe it will be useful to share my solution.
If you have imported java.util.Map and need to use scala.collection.immutable.Map then use it with the full name so instead of
Map(1 -> "one")
do
scala.collection.immutable.Map(1 -> "one")
This way it will know what you mean
I got similar error on Lists. try this in scala console:
import java.util.List object test { def a():List[String] = { val list = List[String](); null }}
you'll get the err "Object List is not a value."
You get it because you're hiding the built-in List type, that is because List is different from java.util.List
What if someone wants to use util.List?
you can use a qualified name or a rename import
! import java.util.{List => JList}
import java.util.{List=>JList}
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
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]])