-
-
Save domdorn/fb504a510ffda470991b to your computer and use it in GitHub Desktop.
package helpers; | |
import org.jooq.DSLContext; | |
import java.util.Collection; | |
/** | |
* | |
*/ | |
public class DSLWrapper { | |
private final DSLContext context; | |
public DSLWrapper(final DSLContext context) { | |
this.context = context; | |
} | |
public int execute(String sql, Collection<? extends Object> arguments) { | |
return context.execute(sql, arguments.toArray()); | |
} | |
} |
[info] Compiling 1 Scala source and 1 Java source to /home/domdorn/lyrix/lyrix_php/playlyrix/target/scala-2.11/classes... | |
[error] /home/domdorn/lyrix/lyrix_php/playlyrix/app/controllers/Security.scala:54: overloaded method value execute with alternatives: | |
[error] (x$1: String,x$2: org.jooq.QueryPart*)Int <and> | |
[error] (x$1: String,x$2: Object*)Int | |
[error] cannot be applied to (String, Long, String) | |
[error] e.execute(sql, userId, uuid) | |
[error] ^ | |
[error] one error found | |
[error] (compile:compile) Compilation failed |
val userId = 1L | |
val uuid = UUID.randomUUID().toString.replace("-", "") | |
val sql = "INSERT INTO sometable (userId, hash) VALUES (?, ?);" | |
DB.withTransaction(conn => { | |
val e = DSL.using(conn, SQLDialect.POSTGRES) | |
e.execute(sql, userId, uuid) // does not work | |
// e.execute(sql, List(userId, uuid) : _*) // does not work either | |
}) | |
val e = DSL.using(conn, SQLDialect.POSTGRES) | |
val l : java.util.ArrayList[Object] = new java.util.ArrayList[Object]() | |
l.add(java.lang.Long.valueOf(userId)) | |
l.add(uuid) | |
new DSLWrapper(e).execute(sql, l) |
Speculating a bit, but most likely the problem is that the least upper bound of ""
and 12
in Scala is Any
, which does not conform to Object
(see Scala type hierarchy, Object
is an alias for AnyRef
)
Therefore, fun(1, "", 12)
fails because the vararg parameter type is Object...
.
This works:
fun(1, "", 12: Integer)
The type annotation will force the Scala compiler to insert a boxing conversion, and the LUB of String
and Integer
is Object
.
@lrytz: That's very interesting. I suspect that this issue only appears when interoperating with a Java library. Is there any way to enforce or favour some implicit conversion to Object
? I.e. is there anything that can be done from a library side (we already have jOOQ-Scala, a rather trivial module that contains some implicit conversions)
Thanks Lukas for chiming in!
I've opened an issue to track this case: https://issues.scala-lang.org/browse/SI-8800
@lukaseder: Actually, you can call it a bug in Scala's Java-interopability. Even though in Scala's type system, java.lang.Object
is an alias for scala.AnyRef
, when reading a classfile coming from Java, the Scala compiler maps Object
to Any
. Why? Probably to simplify Java interop. Discussed here on SO.
Now, the Scala compiler does NOT map Object varargs in Java classfiles to Any varargs.
public class Test {
public void f(Object o) {
System.out.println("obj");
}
public void g(Object... o) {
System.out.println("obj...");
}
}
scala> val t = new Test
t: Test = Test@1330b682
scala> t.f(1) // works because the Scala compiler thinks f: (Any): Unit
obj
scala> t.g(1) // Scala compiler thinks g: (AnyRef*): Unit
<console>:9: error: the result type of an implicit conversion must be more specific than AnyRef
t.g(1)
^
You can call that a bug, I guess. I added it to the ticket SI-8800.
@ktoso: Thanks for opening that issue.
@lrytz: Interesting. I guess the g(Object...)
case is really sign for a compiler bug. I wonder how type inference with generic methods plays into this kind of interoperability, i.e. when the signature is something like:
public <T> void h(T... o) {
System.out.println("t...");
}
On a bytecode level, T...
is really erased again to Object[]
, but on a language level, there is a lot of (false) type-safety and type-inference that is applied. I'm saying false, because any list of parameters will be viable, in principle.
This becomes particularly interesting when the T
type has side-effects on the call site, such as:
public <T> T h(T... o) {
System.out.println("t...");
return o != null && o.length > 0 ? o[0] : null;
}
I wonder if this would produce the same behaviour in Scala as in Java, e.g. (pseudo-repl):
scala> 1 == t.h(1)
true
I've also copied my comment over to SI-8800
@lukaseder: Yes, that works:
scala> t.h(1) == 1
t...
res1: Boolean = true
Here's a workaround:
The reason is as you noticed, funky interplay between Scala Seq/Array and Java's representation of varargs... Can't think of a better workaround at this moment.
Hope this helps.