【问题标题】:Scala optionally supply named default parameterScala 可选地提供命名默认参数
【发布时间】:2015-09-22 18:27:03
【问题描述】:

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

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类中的字段是vals,所以实例化A后我不能改变它们。

【问题讨论】:

  • 为什么不只是val x = if (condition) new A(5, 5) else new A(5)
  • 我建议不要在超过几个值的情况下使用默认参数。当您忘记设置值时,编译器将不再能够告诉您。随着代码库的成熟和新价值的添加,这种做法不可避免地会导致错误,至少这是我的经验。仅对于少数几个值,组合爆炸 N/A。默认参数失败的一个很好的例子是 akka 1.3.x 中的超时。
  • @drstevens - OP 无法控制被调用的类;他正试图弄清楚如何处理它。
  • 这似乎是构建器模式适用的问题类别。
  • 我写了一篇博客文章,其中包含一个宏解决方案,它提供了这种语法here

标签: scala


【解决方案1】:

在我看来你有三种可能:

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

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

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

【讨论】:

    【解决方案2】:

    获取默认值,

    val defaultA = new A()
    

    然后

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

    【讨论】:

      【解决方案3】:

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

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

      “错误”的部分是这在 2.10 中有效,但在 2.9 中无效。默认方法的魔法名称已从版本更改为版本(特别是在 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

      【讨论】:

        【解决方案4】:

        如果像这样从 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))
        

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

        【讨论】:

          【解决方案5】:

          我不禁觉得你的要求有点不合理。如前所述,宏应该是可能的。但是通常如果您对提供的构造函数/工厂不满意,那么您必须编写自己的包装器/工厂。就我个人而言,我认为我们可能希望重新审视默认值、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 
          

          【讨论】:

            猜你喜欢
            • 2012-05-26
            • 2012-03-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-08-14
            • 2022-01-16
            • 2011-05-11
            • 1970-01-01
            相关资源
            最近更新 更多