从Java过滤Scala列表

时间:2012-12-13 00:08:41

标签: scala

这编译很好,但在运行时会爆炸:

  

线程“main”中的异常java.lang.NoSuchMethodError:scala.collection.immutable.List.filter(Lscala / Function1;)Lscala / collection / immutable / List

import scala.Function1;
import scala.collection.immutable.List;
import scala.collection.immutable.Nil$;
import scala.runtime.AbstractFunction1;

public class FunProc {
    List nil = Nil$.MODULE$;                      // the empty list
    List<Integer> list1 = nil.$colon$colon(1);    // append 1 to the empty list
    List<Integer> list2 = list1.$colon$colon(2);  // append 2 to List(1)
    List<Integer> list3 = list2.$colon$colon(3).$colon$colon(14).$colon$colon(8); // List(8, 14, 3, 2, 1)

    Function1<Integer, Object> filterFn = new AbstractFunction1<Integer, Object>() {
        public Boolean apply(Integer value) { return value<10; }
    };

    List<Integer> list4 = list3.filter(filterFn); // List(8, 3, 2, 1)

    public void doIt() {
        System.out.println("Filtered List is " + list4);
    }
}

修改

在尝试了idonnie的回答之后,我想出了这个:

List<Integer> list4 = list3.toTraversable().filter(filterFn).toList();

这与idonnie的答案基本相同,只是使用转换而不是强制转换。我仍然想知道为什么toTraversable()是必要的,因为以下编译很好:

List<Integer> list4 = list3.filter(filterFn);

4 个答案:

答案 0 :(得分:5)

对我来说,

$ javac -cp O\:/scala-2.10.0-RC2/lib/scala-library.jar  jfilter/FunProc.java 
jfilter\FunProc.java:19: error: incompatible types
    List<Integer> list4 = list3.filter(filterFn); // List(1, 2, 3, 8)
                                      ^
  required: List<Integer>
  found:    Traversable<Integer>
Note: jfilter\FunProc.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

idonnie的Traversable还可以。

FWIW,scaladoc为那些厌恶IDE的人展示了“定义类”。

这可能是inconsistent java generics signature的症状,discussionanalysis interminable

scalac -Ycheck:jvm TraversableLike.scala并没有抱怨过滤器。 (我对ML线程的评论是“编译器在发出警告后退出addGenericSignature”。我的修复方法是在Scala中添加一个强制转换来支持互操作。)

答案 1 :(得分:2)

通过javap,终于找到了scala.collection.TraversableLike中的过滤方法

修改

编译并运行:

    List<Integer> list4 = (List<Integer>)
(((scala.collection.TraversableLike) list3).filter(filterFn));

答案 2 :(得分:2)

我们可以使用FunctionN及其AbstractFunctionN子类在Java上的Scala列表中执行filter()等操作。以下是创建Scala列表并对其进行过滤的方法。

// List(8, 3, 2, 1)
List<Integer> list4a = list3.toTraversable().filter(filterFn).toList(); 

此技术可以访问TraversableLike特征提供的方法,以便在列表上调用toTraversable(),从而返回Traversable<Integer>个实例。然后可以调用filter(),返回另一个Traversable<Integer>实例。 Traversable有一个名为toList()的方法,然后将Traversable<Integer>实例转换回List<Integer>

我不明白som-snytt引用的Scala编译器错误如何通过以下方式得到缓解:

// this produces the same result: List(8, 3, 2, 1)
List<Integer> list4b = 
    (List<Integer>) ((scala.collection.TraversableLike) list3).filter(filterFn));

filterFn()Function1子类AbstractFunction1的一个实例。此实例的定义为Java Function1<Integer, Object>,这意味着它接受Integer并返回Object。在Scala中编写的apply()方法定义实际上返回scala.Boolean,但Java确实not know about scala.Boolean . Instead, we define apply()to return a java.lang .Boolean和返回值的类型参数声明为java.Object我想了解这项工作的正确性。

答案 3 :(得分:1)

这听起来很可疑。

首先,我要说明使用特征不应该与它有任何关系。当您使用traits时,Scala会为实现这些traits的静态方法创建转发器,因此调用由trait或class定义的方法应该没有任何区别。

其次,我认为filter给出的javap定义没有问题:

  public scala.collection.immutable.List<A> filter(scala.Function1<A, java.lang.Object>);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0       
         1: aload_1       
         2: invokestatic  #1103               // Method scala/collection/TraversableLike$class.filter:(Lscala/collection/TraversableLike;Lscala/Function1;)Ljava/lang/Object;
         5: areturn       
      LineNumberTable:
        line 76: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       6     0  this   Lscala/collection/immutable/List;
               0       6     1     p   Lscala/Function1;
    Signature: #1104                        // (Lscala/Function1<TA;Ljava/lang/Object;>;)Lscala/collection/immutable/List<TA;>;

所以方法就在那里,类型签名似乎正确。我会把这个问题带到scala邮件列表,虽然我愿意打赌已经有一些问题已经打开了。