How to pass implicit vals defined through package objects from Scala in Java

Alexandr :

I am using the Cats library. In Scala the code looks like:

import cats.Semigroupal
import cats.instances.option._
val r = Semigroupal.tuple2(Option(1), Option(2))

The tuple2 defined as:

def tuple2[F[_], A0, A1](f0:F[A0], f1:F[A1])(implicit semigroupal: Semigroupal[F], invariant: Invariant[F]):F[(A0, A1)]

The following implicit value is actually passed both as Semigroupal and as Invariant (checked via Intellij IDEA plugin for Scala):

package cats
package instances

trait OptionInstances ...
  implicit val catsStdInstancesForOption: ...

How to pass catsStdInstancesForOption in tuple2 function from Java code?

Semigroupal$.MODULE$.tuple2(
  Option.apply(1), Option.apply(2),
  ...,// and here?
  ... //here 
  );

Dependency on the Cats library, if needed:

<cats.core.version>1.5.0</cats.core.version>
...
<!-- https://mvnrepository.com/artifact/org.typelevel/cats-core -->
<dependency>
  <groupId>org.typelevel</groupId>
  <artifactId>cats-core_2.12</artifactId>
  <version>${cats.core.version}</version>
</dependency>
Travis Brown :

Referring to Scala objects defined inside package objects from Java is a pain, and as far as I can tell there's no good reason for this—it's entirely because of the way the compiler decides to mangle names (e.g. you can't refer directly to cats.instances.package$option$ from Java).

I've wanted to do this kind of thing before, and the best solution I've found is something like this:

import cats.Apply;
import cats.Semigroupal$;
import cats.instances.OptionInstances;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import scala.Option;
import scala.Tuple2;

public class Test {
  public static Apply<Option> getOptionInstance() {
    try {
      Class<?> cls = Class.forName("cats.instances.package$option$");
      Field f = cls.getField("MODULE$");
      Method m = f.getType().getMethod("catsStdInstancesForOption");
      return (Apply<Option>) m.invoke(f.get(null));
    } catch (Exception e) {
      // Shouldn't happen but do something here anyway.
      return null;
    }
  }

  public static void main(String[] args) {
    Apply<Option> optionInstance = getOptionInstance();

    Option<Tuple2<Integer, Integer>> pair = Semigroupal$.MODULE$.tuple2(
      Option.apply(1),
      Option.apply(2),
      optionInstance,
      optionInstance
    );

    System.out.println(pair);
  }
}

It's terrible, but at least the reflection and casting are bundled up in one place.

If you have lots of instances you need to use from Java, you could abstract some of the logic out of getOptionInstance to cut down on repetition, but it's still not going to be fun. If you can write some utility code on the Scala side to use from Java, it wouldn't be too hard to make a more Java-friendly cats.instances—just don't use package objects, etc. (For what it's worth I wish Cats had kept more of an eye on Java-friendliness during its development, but as far as I can tell nobody else cared about that at all.)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=80075&siteId=1