首页 文章

(case)类构造函数的上下文中的隐式转换

提问于
浏览
7

我想让case类的自动伴侣类 apply 构造函数为我执行隐式转换,但无法弄清楚如何执行此操作 . 我一直在搜索,我能找到的最接近的答案是this问题(我在下面查找的是'll explain why it isn') .

我有一个类似于下面的案例类:

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

我希望能够让构造函数自动将布尔参数转换为longs( 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

我尝试在伴随对象中添加隐式转换(下面),希望它会自动在_671111中使用,但看起来这实际上并没有将隐式转换放入调用apply的代码的命名空间中 .

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

我能够使用这种hackish变通方法来解决问题:

{
  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 时我都会免费获得隐式转换,但不是吗?或者由于某种技术限制,这是不可能的?

1 回答

  • 6

    你可以使用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 的相当可怕的一般转换 .

相关问题