6

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)

2 Answers 2

7

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
Sign up to request clarification or add additional context in comments.

2 Comments

I tried it with your example it does seem to work. hmm not sure why calling that guice servlet's method makes a difference.
interesting! I might report this as a bug
2

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

3 Comments

thanks for your answer. String*'s type is WrapperArray and toArray won't change that. so this will result in the first error message
Strange, for me something like System.out.printf("%s %s", Array("Hello","World"):_*) works fine.
I had the same question about varargs, this answer solved my problem!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.