平面映射AnyRef *后,Scala无法解析正确的重载SLF4J方法

时间:2014-06-11 05:23:25

标签: scala slf4j scala-java-interop

我有一个如下所示的Java界面:

interface Bazz {
   void bar(String msg, Object args...);
}

我想实现该接口(恰好在Scala中)并使用SLF4J日志记录来记录args参数(出于这个问题,使用另一个Scala日志库不是一个选项)。

以下(设计的)scala代码:

object Foo extends Bazz {

  private val log = LoggerFactory.getLogger(Main.getClass)

  def bar(args: AnyRef*) {
    log.info("Processing params: {}, {}, {}", args: _*)
    // ... do stuff with args...
  }
}

object Main extends App {

  val arr: Seq[String] = Array("A","B","C")
  val anyRef: AnyRef = arr  

  Foo.bar(arr)

}

}

运行Main,我得到以下输出:

22:49:54.658 [run-main-0] INFO: sample.Main$ Processing params: WrappedArray(A, B, C), {}, {} 

这并不好,因为:_*正在爆炸args,但第一个元素是一个数组。由于我不会进入这里的原因,我实际上需要将该数组的元素作为单个Object[]参数传递给SLF4J的Logger.info(String, Object[])方法。

这是我的第一次尝试:

def bar(args: AnyRef*) {
  val flatArgs = args.flatMap {
    case s: Seq[_] => s
    case x         => Seq(x)
  }
  log.info("Processing params: {}, {}, {}", flatArgs: _*)
  // ... do stuff with args...
}

这不会导致以下错误:

[error] Main.scala:18: overloaded method value info with alternatives:
[error]   (x$1: org.slf4j.Marker,x$2: String,x$3: <repeated...>[Object])Unit <and>
[error]   (x$1: org.slf4j.Marker,x$2: String)Unit <and>
[error]   (x$1: String,x$2: Throwable)Unit <and>
[error]   (x$1: String,x$2: <repeated...>[Object])Unit <and>
[error]   (x$1: String,x$2: Any)Unit
[error]  cannot be applied to (String, Any)
[error]     log.info("Processing params: {}, {}, {}", flatArgs: _*)
[error]         ^
[error] one error found

如何更改此代码以使其编译并打印Processing params: A, B, C

3 个答案:

答案 0 :(得分:2)

此处的问题是IntAnyVal,这意味着它不是AnyRef/Object 此外,Array不是TraversableOnce

现在在foo方法中,我们在varargs上进行模式匹配。

def foo(msg:String,varargs:AnyRef*) { varargs.toList match {
  case (h:TraversableOnce[_]) :: Nil => log.info(msg, h.toSeq.asInstanceOf[Seq[AnyRef]]:_*)
  case (h:Array[_]) :: Nil => log.info(msg, h.toSeq.asInstanceOf[Seq[AnyRef]]:_*)
  case _ => log.info(msg,varargs:_*)
}}

我们处理的案件有3起:

case (h:TraversableOnce[_]) :: Nil => log.info(msg, h.toSeq.asInstanceOf[Seq[AnyRef]]:_*)

TraversableOnce是Scala集合的基本特征之一;我所知道的每个集合都扩展了这个(Array不是一个集合,它不会扩展它)。它可以包含AnyValAnyRef,但运行时的实际项将是包原始的(java.lang.Integer等)。所以我们可以在Seq[Any]Seq[AnyRef]投降,我们应该没问题。

case (h:Array[_]) :: Nil => log.info(msg, h.toSeq.asInstanceOf[Seq[AnyRef]]:_*)

就像我们使用TraversableOnce一样,将其转换为Seq然后向下转换。转换为Seq将为我们包装任何原语。

case _ => log.info(msg,varargs:_*)

varargs可能为空或包含多个条目的一般情况。

答案 1 :(得分:2)

这是我重新说明(现在很明显)问题的答案。抱歉前面没有明确的问题:

def bar(args: AnyRef*) {
  val flatArgs = args.flatMap {
    case s: Seq[_] => s
    case x         => Seq(x)
  }.toArray.asInstanceOf[Array[Object]]

  log.info("Processing params: {}, {}, {}", flatArgs: _*)
}

注意:如果没有asInstanceOf[Array[Object]],则会发生以下情况:

[error] type mismatch;
[error]  found   : Array[Any]
[error]  required: Array[Object]
[error] Note: Any >: Object, but class Array is invariant in type T.
[error] You may wish to investigate a wildcard type such as `_ >: Object`. (SLS 3.2.10)
[error]   }.toArray
[error]     ^
[error] one error found

答案 2 :(得分:1)

ggovan的更简单变化,在调用foo之前进行匹配:

val arr: Seq[Int] = Array(1,2,3)
val anyRef: AnyRef = arr 

val msg = "Another message: {}, {}, {}"
  anyRef match {
    case seq: Seq[_] => foo(msg, seq.asInstanceOf[Seq[AnyRef]]: _*)
    case x => foo(msg, x)
  }