如何使用 Scala 的 AnyVal 调用 Java 可变参数方法?

Posted

技术标签:

【中文标题】如何使用 Scala 的 AnyVal 调用 Java 可变参数方法?【英文标题】:How to call a Java varargs method with Scala's AnyVal? 【发布时间】:2016-01-15 01:33:51 【问题描述】:

我有一个使用以下签名的 Java 方法(来自 jOOQ):

Query query(String sql, Object... bindings);

现在,我想在 Scala 中用AnyVals 调用这个方法,例如:

val id = 2
val value = 3.14
query(someString, id, value)

这样做会给我以下编译器错误:

Error:(34, 16) overloaded method value query with alternatives:
  (x$1: String,x$2: org.jooq.QueryPart*)org.jooq.Query <and>
  (x$1: String,x$2: Object*)org.jooq.Query
 cannot be applied to (String, Int, Double)
        create.query(someString, id, lat)
               ^

但是,我可以将每个 AnyVal 显式转换为 AnyRef 以强制执行自动装箱,从而消除编译器错误:

query(someString, id.asInstanceOf[AnyRef], value.asInstanceOf[AnyRef])

意识到这一点,我虽然可以编写一个小函数,将所有对象转换为 AnyRef,如下所示:

def toVarargs(arg: Any, args: Any*): Array[AnyRef] = 
    (arg +: args).map(_.asInstanceOf[AnyRef]).toArray


query(someString, toVarargs(id, value))

遗憾的是,这不起作用,因为 Array[AnyRef] 被解释为单个 varargs 变量(将其更改为 Array[Object] 没有任何区别)。

可以使用_* 运算符来完成这项工作:

query(someString, toVarargs(id, value): _*)

但是,这似乎相当冗长。有没有更优雅的解决方案来解决这个问题,或者我通过将我自己的函数与可变参数运算符相结合而陷入困境?

【问题讨论】:

【参考方案1】:

在 jOOQ 3.7 中,Conversions type of the jOOQ-scala extension 中有一个新的 SQLInterpolation 类。它本质上只是:

implicit class SQLInterpolation(val sc : StringContext) extends AnyVal 

  def sql(args: Any*) : SQL = 
    DSL.sql(string, args.asInstanceOf[Seq[AnyRef]] : _*)

  def condition(args : Any*) : Condition = 
    DSL.condition(string, args.asInstanceOf[Seq[AnyRef]] : _*)

  def table(args : Any*) : Table[Record] = 
    DSL.table(string, args.asInstanceOf[Seq[AnyRef]] : _*)

  def query(args : Any*) : Query = 
    DSL.query(string, args.asInstanceOf[Seq[AnyRef]] : _*)

  def resultQuery(args : Any*) : ResultQuery[Record] = 
    DSL.resultQuery(string, args.asInstanceOf[Seq[AnyRef]] : _*)

  private def string = 
    val pi = sc.parts.iterator
    val sb = new StringBuilder(pi.next())
    var i = 0;

    while (pi.hasNext) 
      sb += ''
      sb ++= (i toString)
      sb += ''
      sb ++= pi.next()

      i = i + 1;
    

    sb.result
  

使用这个,你应该可以写:

val id = 2
val value = 3.14
query"""SELECT * FROM t WHERE id = $id and value = $value"""

上述方法的好处在于,它既适用于上述绑定值,也适用于嵌入式 SQL:

val id = 2
val value = 3.14
val condition = (MY_TABLE.ID === id) and (MY_TABLE.VALUE === value)
query"""SELECT * FROM t WHERE $condition"""

【讨论】:

这解决了我最初的问题。但是我发现您没有找到比使用args.asInstanceOf[Seq[AnyRef]] : _* 更简单的解决方案。另外,您将args 传递给string(args) 而不使用它们的原因是什么? (可能缺少sc.checkLengths(args) 声明?) @r0estir0bbe: 是的,sc.checkLengths(args) 调用是它...它最终被删除,因为这个检查不是对 jOOQ 的 API 的严格要求 最后我实际上能够在 Scala 中测试 jOOQ 的字符串插值功能。但是,我遇到了无法设置DSLContext 的问题,因此收到Cannot execute query. No Connection configured 错误。我做错了什么/有什么方法可以预先设置DSLContext?我尝试同时使 ConfigurationDSLContext 隐含,但这对我不起作用。 @r0estir0bbe:从 cmets 很难说。您介意提出一个新问题(带有代码示例)吗? 感谢您的快速答复!链接到follow-up question。

以上是关于如何使用 Scala 的 AnyVal 调用 Java 可变参数方法?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 AnyVal (Int, Double) 不能是 scala.Serializable 参数的实际参数

AnyVal与AnyRef

如何使函数与Scala中的任何数字类型的集合一起使用

Scala 类型层次结构

Scala-Numbers

ScalaScala之Numbers