2

I need a method, which gets java Map and convert it to scala immutable Map. I have tried to do this

import scala.Predef;
import scala.Tuple2;
import scala.collection.JavaConverters;

public static <K,V> scala.collection.immutable.Map<K,V> convert(java.util.HashMap<K,V> javaMap) {
       return JavaConverters.mapAsScalaMapConverter(javaMap).asScala().toMap(Predef.<Tuple2<K,V>>conforms());
}

But I am getting following message

Required type: scala.$less$colon$less<Tuple2<K, V>,scala.Tuple2<K,V>>

Provided: scala .Predef.$less$colon$less<Tuple2<K, V>,scala.Tuple2<K,V>>

I need to solve this problem using Java language, and I am not able to write any Scala code upd: Also I ve tried casting, but I ve been getting java.lang.ClassCastException: java.util.HashMap cannot be cast to scala.collection.immutable.Map

1
  • I think the easiest way would be to: 1) actually write this code in Scala, 2) compile it, 3) check with javap what bytecode was generated and use that knowledge in Java. OTOH I think map converted expects an implicit evidence A <:< (K, V), which in Java you'd have to instantiate and pass by hand. Here, additionally there might be the issue that Scala understands the idea of a type alias and Java not really, so you might have 2 different types, where in Scala there is only 1 type with 2 names. Perhaps casting the value returned by Predef.conforms would be enough. Commented Sep 1, 2023 at 16:03

1 Answer 1

2

I wrote the following Scala code:

import scala.collection.JavaConverters._

class MapConversion {

  def fromScala[K, V](map: scala.collection.immutable.Map[K, V]): java.util.Map[K, V] = map.asJava

  def toScala[K, V](map: java.util.Map[K, V]): scala.collection.mutable.Map[K, V] = map.asScala
}

and compiled it with

scala-cli compile --scala "2.12.11" .

(JavaConverters suggest Scala 2.12 or older).

Then I opened the resulting MapConversion.class in https://dmitriy-gulyaev.github.io/java-disassembler/ although I could just use javap as well.

Reading the bytecode suggest that the closest Java code would be something similar to:

class MapConversion {

  <K, V> java.util.Map<>K, V> fromScala(
    scala.collection.immutable.Map<K, V> map
  ) {
     return
        // get JavaConverters$ companion object instance
        scala.collection.JavaConverters$.MODULE$
        // use implicit conversion to wrap it in Decorators$AsJava
          .mapAsJavaConverter(map)
          // call asJava to convert the value
          .asJava 
  }

  <K, V> scala.collection.mutable.Map<K, V> toScala(
    java.util.Map<K, V> map
  ) {
    return
      // get JavaConverters$ companion object instance
      scala.collection.JavaConverters$.MODULE$
        // use implicit conversion to wrap it in Decorators$AsScala
        .mapAsScalaMapConverter(map)
        // call asScala to convert the value
        .asScala
  }
}

(Disclaimer: I haven't tested it, and I don't intend to).

However, 99% of the time the requirement to "not use Scala language" would be hard to defend: it dismiss the right tool for the job (compiler), it requires reverse engendering which in turns requires the knowledge how JVM works, and it requires such reverse engineering every time any change is made. It also doesn't "save" the codebase from "Scala pollution" since apparently Scala standard library and code using it are already in the dependencies.

If the company as a whole don't want to deal with Scala, that's fine. But such issues can be much easier solved by:

  1. defining an interface:
interface JavaToScalaAdapters {
  // ...
}
  1. implementing it in Scala as a top-level class with no constructor arguments, globals, etc
class JavaToScalaAdaptersImpl extends JavaToScalaAdapters {
  // ...
}
  1. calling new in Java to obtain this implementation and call it like any other Java code
JavaToScalaAdapters adapters = new JavaToScalaAdaptersImpl();

It could be a single artifact used in all companies Java projects, which rely on Scala even when they don't want to. Any other solution is wasting a lot of time and money. Even if it was a one-time workaround it is just faster to write it in Scala, compile, and throw .class file into resources directory in version control, than reverse engineer the code. The only justifiable cases where such reverse engineering would be justified would be learning or developing some low-level utilities.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.