I would like to call the following java method from scala:
protected final FilterKeyBindingBuilder filter(String urlPattern, String... morePatterns) {
return filtersModuleBuilder.filter(Lists.newArrayList(urlPattern, morePatterns));
}
my scala caller looks like this
def test(url: String, urls: String*) {
filter(url, urls: _*).through(classOf[MyTestWhateverFilter])
}
this compiles, however, executing the code gives an exception:
java.lang.ClassCastException: scala.collection.mutable.WrappedArray$ofRef cannot be cast to [Ljava.lang.String;
I also tried this:
def test(url: String, urls: String*) {
filter(url, urls.map(_.asInstanceOf[java.lang.String]) :_*).through(classOf[MyTestWhateverFilter])
}
in this case the exception was:
java.lang.ClassCastException: scala.collection.mutable.ArrayBuffer cannot be cast to [Ljava.lang.String;
I thought that in 2.8 Array[String] is passed to java as String[] array and no extra unboxing is necessary.
Any ideas?
Thanks in advance!
EDIT:
how to replicate it:
import com.google.inject.servlet.ServletModule
trait ScalaServletModule extends ServletModule{
def test(s: String,strs: String*) = {
println(strs.getClass)
println(super.filter(s,strs:_*))
}
}
object Test {
def main(args: Array[String]) {
val module = new ServletModule with ScalaServletModule
module.test("/rest")
}
}
/opt/local/lib/scala28/bin/scala -cp /Users/p.user/Downloads/guice-2.0/guice-2.0.jar:/Users/p.user/Downloads/guice-2.0/guice-servlet-2.0.jar:/Users/p.user/Downloads/guice-2.0/aopalliance.jar:/Users/p.user/Downloads/javax.jar/javax.jar:. Test
result:
class scala.collection.mutable.WrappedArray$ofRef
java.lang.ClassCastException: scala.collection.mutable.WrappedArray$ofRef cannot be cast to [Ljava.lang.String;
at ScalaServletModule$class.test(test.scala:6)
at Test$$anon$1.test(test.scala:11)
at Test$.main(test.scala:12)
at Test.main(test.scala)
I've just tried to reproduce your error using Scala 2.8.0 and can't. Here's my code
// Example.java
public class Example {
public static void test(String... args) {
System.out.println(args.getClass());
}
}
// In test.scala
object Test {
def main(args: Array[String]) {
test("1", "2", "3")
}
def test(strs: String*) = {
println(strs.getClass)
Example.test(strs:_*)
}
}
I get the following output:
class scala.collection.mutable.WrappedArray$ofRef
class [Ljava.lang.String;
So it looks like the compiler is inserting the correct conversion to convert the WrappedArray.ofRef
to a String[]
.
Edit
Just tried running your example. It looks like some interaction of super-accessors in traits with converting Scala varargs to Java varargs. If you change the trait to a class it works.
From the decompiled output of ScalaServletModule$class
, it looks like it doesn't do the necessary conversion from String*
to String[]
when calling the super accessor (line 19).
public static void test(ScalaServletModule, java.lang.String, scala.collection.Seq);
Code:
0: getstatic #11; //Field scala/Predef$.MODULE$:Lscala/Predef$;
3: aload_2
4: invokevirtual #18; //Method java/lang/Object.getClass:()Ljava/lang/Class;
7: invokevirtual #22; //Method scala/Predef$.println:(Ljava/lang/Object;)V
10: getstatic #11; //Field scala/Predef$.MODULE$:Lscala/Predef$;
13: aload_0
14: aload_1
15: aload_2
16: checkcast #24; //class "[Ljava/lang/String;"
19: invokeinterface #30, 3; //InterfaceMethod ScalaServletModule.ScalaServletModule$$super$filter:(Ljava/lang/String;[Ljava/lang/String;)Lcom/google/inject/servlet/ServletModule$FilterKeyBindingBuilder;
24: invokevirtual #22; //Method scala/Predef$.println:(Ljava/lang/Object;)V
27: return
The Scala and Java approach for varargs doesn't match: Scala varargs are based on Seqs (or so?) and Java varargs on arrays. Did you try
filter(url, urls.toArray:_*).through(classOf[MyTestWhateverFilter])
?
At least this seems to have worked here: Using varargs from Scala
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