【问题标题】:Calling Scala Monads from Java #map从 Java #map 调用 Scala Monad
【发布时间】:2012-07-27 16:33:10
【问题描述】:

我有一个 scala.collection.immutable.List 的实例,我想在其上调用 map 方法,但来自 Java。

我需要提供CanBuildFrom

我注意到许多 scala 集合伴随对象包含隐式 CanBuildFrom 实例,但我无法确定我需要使用哪一个。

这是我的 Java 代码:

    Function1<WeatherData, BigDecimal> mapper = new AbstractFunction1<WeatherData, BigDecimal>(){
        @Override
        public BigDecimal apply(WeatherData data) {
            return data.getTemps().reduce(adder).divide(new BigDecimal(data.getTemps().size()));
        }
    };

    scala.collection.immutable.List<WeatherData> data = ...

    data.map(mapper, ???);

我应该传递什么作为 CanBuildFrom(第二个参数?)

PS 使用 Scala 2.10-M5

【问题讨论】:

    标签: scala scala-collections


    【解决方案1】:

    您实际上可以在 Java 中获得正确的类型而无需大惊小怪:

    import scala.collection.Traversable;
    import scala.collection.generic.CanBuildFrom;
    import scala.collection.immutable.List;
    import scala.collection.mutable.Builder;
    import scala.runtime.AbstractFunction1;
    
    public class ScalaMapTest {
      public static List<Integer> parseInts(List<String> xs) {
        final CanBuildFrom<List<?>, Integer, List<Integer>> builder =
          List.<Integer>canBuildFrom();
    
        return xs.map(
          new AbstractFunction1<String, Integer>() {
            public Integer apply(String s) {
              return Integer.parseInt(s);
            }
          },
          new CanBuildFrom<Traversable<String>, Integer, List<Integer>>() {
            public Builder<Integer, List<Integer>> apply() {
              return builder.apply();
            }
    
            public Builder<Integer, List<Integer>> apply(Traversable<String> from) {
              return builder.apply(from.toList());
            }
          }
        );
      }
    }
    

    它仍然像罪恶一样丑陋,但它有效。问题是CanBuildFrom 上的通配符是从List 对象上的canBuildFrom 方法获得的,但幸运的是,您可以使用正确的类型创建自己的CanBuildFrom 包装器。

    【讨论】:

    • 我知道它的丑陋,让人流泪,但我是从向一群 Java 人介绍 Scala 的伟大特性的角度出发的,并且想表明你可以将它用作库,但是将它用作一种语言也更有意义!所以它丑陋的事实是一个奖励:-)
    【解决方案2】:

    如果您想知道 scalac 对代码的作用,请询问它。 ;)

    这可以通过scalac -Xprint:typer &lt;file&gt; 或 2.10 中的新反射 API 实现:

    scala> import reflect.runtime.universe._
    import reflect.runtime.universe._
    
    scala> reify{List(1,2,3).map(_+1)}
    res0: reflect.runtime.universe.Expr[List[Int]] = Expr[List[Int]](immutable.this.List.apply(1, 2, 3).map(((x$1) => x$1.$plus(1)))(immutable.this.List.canBuildFrom))
    

    因此,使用此CanBuildFrom 调用map,一切正常。真的吗?不,它没有!问题是 Java 编译器愚蠢地推断map 期望的参数。那么该怎么办?我相信唯一的方法是创造所需的价值,然后他们将它们扔掉。最后混合一些SuppressWarnings-Annotations,代码应该可以正常工作。 ;)

    这是我想出的:

    import scala.Function1;
    import scala.collection.generic.CanBuildFrom;
    import scala.collection.immutable.List;
    import scala.collection.immutable.List$;
    import scala.runtime.AbstractFunction1;
    
    public class JTest {
      @SuppressWarnings({"unchecked", "rawtypes"})
      public static void main(final String... args) {
        final List<Integer> xxx = (List) List$.MODULE$.apply(Predef.wrapIntArray(new int[] {1,2,3}));
        System.out.println(xxx);
    
        System.out.println(Test.sum(1, 2));
        final Abc abc = new Abc();
        System.out.println(abc.hello("simon"));
    
        final List<Integer> xs = (List) Test.xs();
        final Function1<Integer, String> mapper = new AbstractFunction1<Integer, String>() {
          @Override
          public String apply(final Integer i) {
            return String.valueOf(i);
          }
        };
        final CanBuildFrom<List<Integer>, String, List<String>> cbf =
            (CanBuildFrom) List.<Integer>canBuildFrom();
        final List<String> ys = xs.<String, List<String>>map(mapper, cbf);
        System.out.println(ys);
      }
    }
    

    名单:

    object Test {
      def xs = List(1,2,3)
    }
    

    我相信,最好的办法是不要使用 Java 中的 Scala 代码。它看起来很丑。至少,将它包装在一些帮助类中,这样看到这段代码的人就不会想到对他的视网膜进行酸攻击。

    顺便说一句,有时您必须查看字节码才能了解 scalac 如何创建值。如果你想创建一个 Scala List is Java 你必须用javap -c -s -l -verbose -private &lt;classfile&gt;反编译代码:

       0:   aload_0
       1:   invokespecial   #20; //Method java/lang/Object."<init>":()V
       4:   aload_0
       5:   putstatic   #22; //Field MODULE$:LX$;
       8:   aload_0
       9:   getstatic   #27; //Field scala/collection/immutable/List$.MODULE$:Lscala/collection/immutable/List$;
       12:  getstatic   #32; //Field scala/Predef$.MODULE$:Lscala/Predef$;
       15:  iconst_3
       16:  newarray int
       18:  dup
       19:  iconst_0
       20:  iconst_1
       21:  iastore
       22:  dup
       23:  iconst_1
       24:  iconst_2
       25:  iastore
       26:  dup
       27:  iconst_2
       28:  iconst_3
       29:  iastore
       30:  invokevirtual   #38; //Method scala/LowPriorityImplicits.wrapIntArray:([I)Lscala/collection/mutable/WrappedArray;
       33:  invokevirtual   #42; //Method scala/collection/immutable/List$.apply:(Lscala/collection/Seq;)Lscala/collection/immutable/List;
       36:  new #44; //class X$$anonfun$1
    

    或者使用更易读的 Java 代码:

    @SuppressWarnings({"unchecked", "rawtypes"})
    final List<Integer> xs = (List) List$.MODULE$.apply(Predef.wrapIntArray(new int[] {1,2,3}));
    

    【讨论】:

    • 有几件事:这里不需要MODULE$ 业务,因为您实际上不需要object 作为Java 实例,只需要静态方法。您还需要在 2.10 的几个地方将 List 替换为 Traversable
    • @TravisBrown:我尝试在没有MODULE$ 的情况下编译代码,但没有成功。我使用了 eclipse Java 编译器——它在编译使用 Scala 库的代码时存在缺陷。致Traversable:如果这是真的,你能给我举个例子吗?我不敢相信因为List &lt;: Traversable
    • 在我的回答中 List.&lt;Integer&gt;canBuildFrom() 编译并按预期工作。如果您查看我的编辑,您可以看到使 2.10 编译器满意所需的更改。我承认,从 Java 看 Scala 方差的细节对我来说是个谜——我只是按照编译器告诉我的去做,在这种情况下它想要Traversable
    • 是的,但是这里已经有很多间接级别了,那到底是什么,为什么不扔另一个呢?
    • @TravisBrown:我相信我们应该在这里结束这个讨论,并认为这两种解决方案都很丑陋。 ;)
    猜你喜欢
    • 1970-01-01
    • 2016-10-22
    • 2019-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-15
    • 2012-04-10
    • 2019-02-18
    相关资源
    最近更新 更多