Scala Map – Initializing a Scala Map with More Than 4 Elements in Java

javamapscala

For 4 or fewer elements, something like this works (or at least compiles):

import scala.collection.immutable.Map;

Map<String,String> HAI_MAP = new Map4<>("Hello", "World",
                                        "Happy", "Birthday",
                                        "Merry", "XMas",
                                        "Bye", "For Now");

For a 5th element I could do this:

Map<String,String> b = HAI_MAP.$plus(new Tuple2<>("Later", "Aligator"));

But I want to know how to initialize an immutable map with 5 or more elements and I'm flailing in Type-hell.

Partial Solution

I thought I'd figure this out quickly by compiling what I wanted in Scala, then decompiling the resultant class files. Here's the scala:

object JavaMapTest {
  def main(args: Array[String]) = {
    val HAI_MAP = Map(("Hello", "World"),
                      ("Happy", "Birthday"),
                      ("Merry", "XMas"),
                      ("Bye", "For Now"),
                      ("Later", "Aligator"))
    println("My map is: " + HAI_MAP)
  }
}

But the decompiler gave me something that has two periods in a row and thus won't compile (I don't think this is valid Java):

scala.collection.immutable.Map HAI_MAP =
        (scala.collection.immutable.Map) 
        scala.Predef..MODULE$.Map().apply(scala.Predef..MODULE$.wrapRefArray(
                scala.Predef.wrapRefArray(
                        (Object[])new Tuple2[] {
                                new Tuple2("Hello", "World"),
                                new Tuple2("Happy", "Birthday"),
                                new Tuple2("Merry", "XMas"),
                                new Tuple2("Bye", "For Now"),
                                new Tuple2("Later", "Aligator") }));

I'm really baffled by the two periods in this:

scala.Predef..MODULE$

I asked about it on #java on Freenode and they said the .. looked like a decompiler bug. It doesn't seem to want to compile, so I think they are probably right. I'm running into it when I try to browse interfaces in IntelliJ and am just generally lost.

Based on my experimentation, the following is valid:

Tuple2[] x = new Tuple2[] { new Tuple2<String,String>("Hello", "World"),
                            new Tuple2<String,String>("Happy", "Birthday"),
                            new Tuple2<String,String>("Merry", "XMas"),
                            new Tuple2<String,String>("Bye", "For Now"),
                            new Tuple2<String,String>("Later", "Aligator") };

scala.collection.mutable.WrappedArray<Tuple2> y = scala.Predef.wrapRefArray(x);

There is even a WrappedArray.toMap() method but the types of the signature are complicated and I'm running into the double-period problem there too when I try to research the interfaces from Java.

Best Answer

The decompiler probably has problems in decompiling class names correctly that contain a $ symbol. The symbol you are searching for is named Predef$:

import scala.Predef;
import scala.Predef$;
import scala.Tuple2;
import scala.collection.immutable.Map;
import scala.collection.mutable.WrappedArray;

public class T {
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public static void main(final String... args) {
    final Tuple2[] ts = { new Tuple2("a", "b") };
    final WrappedArray wa = Predef.wrapRefArray(ts);
    final Map<String, String> map = Predef$.MODULE$.Map().apply(wa);
    System.out.println(map);
  }
}

Thus, I emphasize not to use a decompiler but the internal representation of the AST in scalac:

$ scala -print -e 'Map(("a", "b"))'
...
scala.this.Predef.Map().apply(
  scala.this.Predef.wrapRefArray(
    Array[Tuple2]{new Tuple2("a", "b")}.$asInstanceOf[Array[Object]]()));
...

If you are interested in more information about how to work with the output of scalac then take a look at this question and its answers.