Как передать неявные значения, определенные через объекты пакета из Scala в Java - PullRequest
4 голосов
/ 06 марта 2019

Я использую библиотеку Cats.В Scala код выглядит следующим образом:

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

tuple2 определяется как:

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

Следующее неявное значение фактически передается как Semigroupal и как Invariant (проверено через плагин Intellij IDEA для Scala):

package cats
package instances

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

Как передать catsStdInstancesForOption в tuple2 функцию из кода Java?

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

Зависимость от библиотеки Cats, если необходимо:

<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>

1 Ответ

3 голосов
/ 06 марта 2019

Ссылка на объекты Scala, определенные в объектах пакета из Java, - это боль, и, насколько я могу судить, нет веской причины для этого - это полностью из-за того, как компилятор решает искажать имена (например, вы не можете ссылаться непосредственно к cats.instances.package$option$ из Java).

Я хотел сделать что-то подобное раньше, и лучшее решение, которое я нашел, это что-то вроде этого:

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);
  }
}

Это ужасно, но, по крайней мере, отражение и кастинг связаны в одном месте.

Если у вас есть много экземпляров, которые вам нужно использовать из Java, вы можете абстрагировать некоторую логику от getOptionInstance, чтобы сократить количество повторений, но это все равно будет неинтересно. Если вы можете написать некоторый служебный код на стороне Scala для использования из Java, было бы не сложно сделать более дружественным к Java cats.instances - просто не используйте объекты пакета и т. Д. (Для чего это стоит Хотелось бы, чтобы Cats больше следил за дружественностью Java во время своей разработки, но, насколько я могу судить, никто больше не заботился об этом вообще.)

...