【问题标题】:Implicit conversions in the context of a (case) class constructor(案例)类构造函数上下文中的隐式转换
【发布时间】:2013-10-01 19:01:21
【问题描述】:

我想要一个案例类的自动伴随类apply 构造函数来为我执行隐式转换,但不知道该怎么做。我已经到处搜索了,我能找到的最接近的答案是this 问题(我将在下面解释为什么它不是我要找的)。

我有一个看起来像这样的案例类:

case class Container(a: Long, b: Long, c: Long)

我正在使用容器来计算适用某些条件的实例,因此我希望能够让构造函数自动将布尔参数转换为长整数 (if (boolean) 1L else 0L)。

当然,真实的案例类有很多参数,所以制作我自己的伴生对象并重载apply 以接受Boolean 参数将是乏味且非常重复的。此外,像下面的代码这样的东西并不理想(如果它以某种方式正确实现),因为它只接受布尔参数:

object Container {
  def apply(args: Boolean*) = {
    // doesn't REALLY work since number of arguments not enforced
    Container(args map { if (_) 1L else 0L } toArray: _*)
  }
}
val c1 = Container(1, 0, 1) // works
val c2 = Container(true, false, true) // might be workable if done correctly
val c3 = Container(true, 0, 1) // won't work

我尝试在伴生对象中添加一个隐式转换(如下),希望它会自动在Container.apply 中使用,但似乎这实际上并没有将隐式转换放入调用apply的代码的命名空间中.

object Container {
  implicit def booleanToLong(x: Boolean): Long = if (x) 1L else 0L
}

我能够使用这种骇人听闻的解决方法使事情正常进行:

{
  import Container.booleanToLong
  // all of these now work
  val c1 = Container(1, 0, 1)
  val c2 = Container(true, false, true)
  val c3 = Container(true, 0, 1) // works!!!
}

最大的问题是我必须将booleanToLong 导入到要创建Container 的代码中,因此必须将其放在自己的块中以确保安全(booleanToLong 通常是不可取的)。

最后,使用本身包含隐式转换的隐式参数的解决方案不起作用,因为它需要显式覆盖 apply,从而违背了不重复长参数列表和封送类型的目标。

有没有一种方法可以让我每次创建Container 时都免费获得隐式转换,但不是?还是由于某种技术限制,这不可能?

【问题讨论】:

    标签: scala implicit-conversion implicit


    【解决方案1】:

    您可以使用magnet pattern 的一种变体来使其更安全一些。首先是类型类:

    trait ToLong[A] {
      def apply(a: A): Long
    }
    
    implicit object longToLong extends ToLong[Long] {
      def apply(l: Long) = l
    }
    
    implicit object booleanToLong extends ToLong[Boolean] {
      def apply(b: Boolean) = if (b) 1L else 0L
    }
    

    现在我们只需要一个额外的构造函数:

    case class Container(a: Long, b: Long, c: Long)
    
    object Container {
      def apply[A: ToLong, B: ToLong, C: ToLong](a: A, b: B, c: C) = new Container(
        implicitly[ToLong[A]].apply(a),
        implicitly[ToLong[B]].apply(b),
        implicitly[ToLong[C]].apply(c)
      )
    }
    

    我们可以这样写:

    val c1 = Container(1, 0, 1)
    val c2 = Container(true, false, true)
    val c3 = Container(true, 0L, 1L)
    

    不必介绍从BooleanLong 的相当可怕的一般转换。

    【讨论】:

    • 非常酷的解决方案。它需要显式构造命名所有参数,这太糟糕了,但看起来这是在客户端代码中获得灵活性和安全性的最佳方式(基于缺乏其他响应)。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-18
    • 2013-11-02
    • 2020-06-20
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    相关资源
    最近更新 更多