我有一个如下所示的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
?
答案 0 :(得分:2)
此处的问题是Int
是AnyVal
,这意味着它不是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
不是一个集合,它不会扩展它)。它可以包含AnyVal
或AnyRef
,但运行时的实际项将是包原始的(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)
}