I have an instance of a scala.collection.immutable.List
and I want to call the map
method on it, BUT from Java.
I need to supply a CanBuildFrom
.
I noticed that a lot of the scala collections companion objects contain implicit CanBuildFrom
instances, but I cannot work out which one I need to use.
Here is my Java code:
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, ???);
What should I pass as a CanBuildFrom (the second parameter?)
PS Using Scala 2.10-M5
You can actually get the types right in Java without too much fuss:
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());
}
}
);
}
}
It's still ugly as sin, but it works. The problem is that wildcard on the CanBuildFrom
you get from the canBuildFrom
method on the List
object, but fortunately you can create your own CanBuildFrom
wrapper with the right type.
If you want to know what scalac does with the code then ask it. ;)
This is possible with either scalac -Xprint:typer <file>
or the new Reflection API in 2.10:
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))
Thus, call map
with this CanBuildFrom
and all works fine. Does it really? No, it doesn't! The problem is that the Java compiler is to silly to infer the arguments expected by map
. So what to do? I believe the only way is to create the required values and them cast them to death. Finally mix some SuppressWarnings-Annotations
in and the code should work fine. ;)
This is what I came up with:
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);
}
}
The list:
object Test {
def xs = List(1,2,3)
}
I believe, the best thing is not to use Scala code from Java. It looks ugly. At least, wrap it in some helper classes so that someone who sees this code does not think about an acid attack on his retina.
By the way, sometimes you have to look at the Bytecode to understand how scalac creates values. If you want to create a Scala List is Java you have to decompile the code with javap -c -s -l -verbose -private <classfile>
:
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
Or in more readable Java code:
@SuppressWarnings({"unchecked", "rawtypes"})
final List<Integer> xs = (List) List$.MODULE$.apply(Predef.wrapIntArray(new int[] {1,2,3}));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With