Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling Java vararg method from Scala with primitives

I have the following code in Java:

public class JavaClass {

    public static void method( Object x ) {
    }

    public static void varargsMethod( Object... x ) {
    }

}

When I try and access it from Scala,

object FooUser {
  JavaClass.method(true)
  JavaClass.varargsMethod(true) // <-- compile error
}

I get the following compile error:

type mismatch; found : Boolean(true) required: java.lang.Object Note: primitive types are not implicitly converted to AnyRef. You can safely force boxing by casting x.asInstanceOf[AnyRef]

The error message is very helpful and shows how to fix the error, but I was wondering why the compiler is (apparently) happy to implicitly convert a scala.Boolean in one method call but not the other. Is this a bug or intentional?

Updated to add: I'm using Scala 2.8. If I make the varargsMethod signature

public static <T> void varargsMethod(T... xs) {

instead, then the error also goes away. I'm still puzzled as to why the compiler can't figure it out.

like image 755
Matt R Avatar asked Jun 11 '10 12:06

Matt R


3 Answers

Note, with Scala version 2.13.x, this works out-of-the-box (no pun) without having to manually box the value.

like image 121
cbley Avatar answered Nov 07 '22 03:11

cbley


Scala varargs and Java varargs are different. You need to do a conversion:

def g(x: Any*) = x.asInstanceOf[scala.runtime.BoxedObjectArray]
.unbox(x.getClass)
.asInstanceOf[Array[Object]]  
...
JavaClass.varargsMethod(g(true))

or (in 2.8.0+)

JavaClass.varargsMethod(java.util.Arrays.asList(true))
like image 42
Vasil Remeniuk Avatar answered Nov 07 '22 03:11

Vasil Remeniuk


Since scala.Boolean is a subclass of scala.AnyVal but not scala.AnyRef (translated to java.lang.Object), a Boolean cannot be passed to a method expecting Object(s).

You can use the companion object scala.Boolean to "box" (in Java's sense, of course) a boolean into java.lang.Boolean:

JavaClass.varargsMethod(Boolean.box(true))

The other AnyVal classes have corresponding box methods (e.g. Int.box). There are also unbox methods to do the opposite.

A more complicated use case:

JavaClass.varargsMethod(Seq(1, 2, 3, 4).map(Int.box): _*) // passes 1, 2, 3, 4

I don't know when these were added to the standard library, but with these you don't have to use the implementation classes of scala.runtime.*.

like image 25
Ohashi Avatar answered Nov 07 '22 03:11

Ohashi