Scala:构造函数接受Seq或varargs

21

我猜测出于兼容性的考虑,变长参数Any*的类型是Array[Any]——如果我错了,请纠正我。但是,这并不能解释以下错误:

class Api(api_url: String, params: Seq[(String, String)]) {
  def this(api_url: String, params: (String, String)*)
    = this(api_url, params.seq)
}

这段代码无法编译,但会显示如下警告:

重复定义:构造函数 Api:(api_url: String, params: (String, String)*)Api 和构造函数 Api:(api_url: String, params: Seq[(String, String)])Api 在第13行被擦除后有相同的类型: (api_url: java.lang.String, params: Seq)Api

那么我应该如何定义一个同时接收可变参数和序列的构造函数呢?

3个回答

33

一个接受可变参数的方法也总是接受一个序列,因此没有必要定义辅助构造函数或重载方法。

给定:

class Api(api_url: String, params: (String, String)*)

你可以这样调用它

new Api("url", ("a", "b"), ("c", "d"))
或者
val seq = Seq(("a", "b"), ("c", "d"))
new Api("url", seq:_*)

此外,在您的问题中,您调用了参数params上的方法seq。这可能不是您想要做的事情。seq用于确保对结果集合的操作以顺序而不是并行方式执行。该方法是在Scala 2.9.0版本中与并行集合一起引入的。

您可能想使用的是toSeq方法,它将被用于将其应用于转换为Seq的集合(或者如果它已经是Seq,则返回自身)。但是,由于varargs参数已经被定义为Seq,因此无论如何都不需要这样做。


6
我可以指出,类型注释 :_* 是真正使得使用序列参数的构造函数调用与可变参数构造函数兼容的关键。更多细节请参考:https://dev59.com/PW025IYBdhLWcg3wW0yx - Johannes Rudolph

10

实际上,Any*Seq[Any]几乎完全相同,而不是与Array[Any]相同。

为了消除二者之间的歧义,您可以使用添加虚拟隐式参数的技术来使签名不同:

class Api(api_url: String, params: Seq[(String, String)]) {
  def this(api_url: String, params: (String, String)*)(implicit d: DummyImplicit) =
    this(api_url, params)
}

1
这不是真的很恶心吗? - wen
5
好的,欢迎提供一个不那么“恶心”的解决方案! - Jean-Philippe Pellet
当我想出一个解决方案时,我会的。现在,我只是暂时放弃了varargs构造函数。^^ - wen
4
为什么不保留可变参数构造函数,并让调用者自己使用 seq:_* 的方法,如果他需要的话? - Derek Wyatt
@Derek:因为我不知道seq:_*的存在。 :) - wen
1
设计新接口时,是应该优先选择 vargargs 还是 Seq - Ivan Balashov

2

我想你希望让方法调用更加美观,因此使用_*进行显式调用不是一个选择。在这种情况下,您可以通过方法重载来解决问题。

class Api(api_url: String, params: Seq[(String, String)]) {
  def this(api_url: String, param : (String, String), params: (String, String)*)
    = this(api_url, param +: params)
  def this(api_url: String)
    = this(api_url, Seq())
}

就我而言,这是最好的解决方案。它允许您执行 Api("url")Api("url", "a" -> "b")Api("url", Seq("a" -> "b")),同时不用通过使用 :_* 来牺牲可读性。 - Eduardo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接