【问题标题】:How to pass implicit vals defined through package objects from Scala in Java如何在 Java 中传递通过包对象从 Scala 定义的隐式 val
【发布时间】:2019-07-27 23:37:16
【问题描述】:

我正在使用 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)]

以下隐式值实际上以 SemigroupalInvariant 的形式传递(通过 Intellij IDEA Scala 插件检查):

package cats
package instances

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

如何在Java代码中的tuple2函数中传递catsStdInstancesForOption

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>

【问题讨论】:

  • 也许您应该提供一个用 Scala 编写的对 Java 更友好的包装方法(可能专门针对您需要的类型)并调用它。
  • @Thilo,我可以。但我想知道,为什么我不能在 Java 中使用catsStdInstancesForOption。我在猫中有数百个具有隐式值的方法,为所有方法编写包装器是无效的。

标签: java scala implicit scala-cats scala-java-interop


【解决方案1】:

从 Java 引用定义在包对象中的 Scala 对象是一件痛苦的事情,据我所知,这没有什么好的理由——这完全是因为编译器决定破坏名称的方式(例如,你不能引用直接从 Java 到 cats.instances.package$option$)。

我以前就想做这种事情,我找到的最好的解决方案是这样的:

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 友好性,但据我所知,没有其他人关心这一点。)

【讨论】:

  • 很好的答案。非常感谢。不幸的是,整个解决方案很丑陋,但它仍然有效!似乎 Scala 的库还应该提供一些帮助方法,以便能够使用 Java 代码中的库类。
猜你喜欢
  • 2016-01-23
  • 2023-03-04
  • 2020-10-22
  • 1970-01-01
  • 2011-04-19
  • 2011-06-22
  • 2016-07-20
  • 2021-01-23
  • 1970-01-01
相关资源
最近更新 更多