9

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

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

假设我无法控制这个类,也无法修改它。我想要做的是用 p1 = 5, p2 = 调用 A 的构造函数(如果 condition1 == true 那么 5 else 默认值)。一种方法是

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 后我无法更改它们。

4

5 回答 5

1

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

  1. 创建变量以保存您要指定的每个值,执行所有代码以填充值,并A在最后实例化一次。正如 Ionut 所提到的,这需要知道默认值。我不认为创建一个一次性对象来读取默认值是那么骇人听闻 - 当然不如嵌入默认值它们 - 但无论如何。

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

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

于 2013-10-17T22:40:55.387 回答
1

获取默认值,

val defaultA = new A()

然后

val x = new A(p1 = 5, p2 = if (cond) 5 else defaultA.p2)
于 2015-09-22T10:27:03.217 回答
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))

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

于 2013-10-18T00:36:07.440 回答
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 
于 2013-10-18T00:55:51.913 回答
0

Here's the quick and sort of wrong answer:

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

The "wrong" part is that this works in 2.10 but not in 2.9. The magick name for the default method has changed from version to version (notably, between 2.9 and 2.10), so it's safer to look up its name and call it via reflection. See Instantiating a case class with default args via reflection, How do I access default parameter values via Scala reflection? and Scala dynamic instantiation with default arguments.

于 2013-10-18T00:11:12.513 回答