Scala可选地提供命名的默认参数

时间:2013-10-17 17:01:11

标签: scala

假设我们有一个类构造函数,它接受具有默认值的参数。

class A(val p1 : Int = 3, val p2 : Int = 4) 

假设我无法控制此类,无论如何都无法修改它。我想要做的是调用A的构造函数,其中p1 = 5,p2 =(如果condition1 == true则为5,否则为默认值)。一种方法是

if(condition1)
  x = new A(5,5)
else
  x = new A(5)

正如您所看到的,如果有许多参数,并且必须有条件地提供每个参数,这很容易变大。我想要的是像

x = new A(p1 = 5, p2 = <if condition1 = true then 5 else default>)

我该怎么做?请注意,A类中的字段是val,因此在实例化A之后我无法更改它们。

5 个答案:

答案 0 :(得分:1)

在我看来,你有三种可能性:

  1. 创建变量以保存您要指定的每个值,执行所有代码以填充值,并在结尾处实例化A一次。这需要知道默认值,如Ionut所述。我不认为创建一个抛弃对象来读取默认值就是那些hackish - 当然不如嵌入默认值那么多 - 但无论如何。

  2. 使用反射API创建A。我不确定如何做到这一点但几乎可以肯定你可以传入一个参数列表,任何未指定的参数都是默认的。这需要Scala 2.10;在此之前,只有Java反射API可用,你必须破解可选参数的内部实现, hackish。

  3. 使用宏。也是2.10+。我认为quasiquotes应该可以毫不费力地做到这一点,虽然我不太熟悉它们所以我不能肯定地说。

答案 1 :(得分:1)

获取默认值

val defaultA = new A()

然后

val x = new A(p1 = 5, p2 = if (cond) 5 else defaultA.p2)

答案 2 :(得分:0)

这是一个快速而错误的答案:

x = new A(p1 = 5, if (condition1) 5 else A.$lessinit$greater$default$2)

“错误”部分是这在2.10中有效但在2.9中不起作用。默认方法的magick名称已从版本更改为版本(特别是介于2.9和2.10之间),因此查找其名称并通过反射调用它更安全。请参阅Instantiating a case class with default args via reflectionHow do I access default parameter values via Scala reflection?Scala dynamic instantiation with default arguments

答案 3 :(得分:0)

如何从A获得派生类:

class D(val t2: (Boolean, Int)) extends A { 
  override val p2: Int = if(t2._1) t2._2 else A.init$default$2
}

现在您可以像这样实例化A:

x = new D((condition1, 5))

如果您有更多参数,则可以为每个参数添加类似的覆盖语句和元组参数。

答案 4 :(得分:0)

我情不自禁地感觉到你的要求有点不合理。如前所述,宏应该是可能的。但通常如果您对提供的构造函数/工厂不满意,那么您必须编写自己的包装器/工厂。就个人而言,我认为我们可能希望看一下整个默认值,Null对象和Option类的问题。但是,可以在没有宏的情况下切出样板。在实用程序包中放置一个隐式布尔值:

implicit class BooleanRich2(n : Boolean) {
  def apply[T](v1: T, v2: T): T = if (n) v1 else v2
}

然后说我们希望使用以下无法修改的类:

final class A(val p1: Int = 1, val p2: Int = 2, val p3: Int = 3, val p4: Int = 4){
  override def toString = s"p1: $p1, p2: $p2, p3: $p3, p4: $p4"
}

我们可以按如下方式使用它:

var c1 = true
var c2 = false
def c3(s: String) = (s =="")

val a = new A(c1(20, 1)) //p1: 20, p2: 2, p3: 3, p4: 4
println(a) //p1: 20, p2: 2, p3: 3, p4: 4

val b = new A(p3 = c2(20, 3), p4 = c3("")(20, 4))
println(b) //p1: 1, p2: 2, p3: 3, p4: 20

val c = new A(p3 = c1(20, 3), p4 = c3("A non empty String")(20, 4))
println(c) //p1: 1, p2: 2, p3: 20, p4: 4