如何在另一个对象之前的对象中隐式设置?

时间:2019-06-22 22:58:50

标签: scala scope implicit

考虑以下情况:

  trait Companion {

    implicit def str(a: A): String =
      s"${this.getClass.getSimpleName}: %d" format a.n
  }

  class A(val n: Int)
  object A extends Companion {}

  class B(val x: Int, y: Int) extends A(y)
  object B extends Companion {}

现在,编译以下代码将触发不同的隐式错误:

val b = new B(5, 2)
val s: String = b
println(s)

因为对象A和AA都在默认的AA隐式范围内。这显然是有缺陷的:AA类比特征A更“精确”,因此它的隐式范围应该具有更高的优先级。不幸的是,由于对象无法彼此继承,因此无法声明这一点。

所以我的问题是:在不诉诸非默认隐式作用域的情况下,实现此目标的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

  

现在,编译以下代码将触发不同的隐式错误:

这不是“发散的隐式错误”,它是模棱两可的,implicit ambiguityimplicit divergence是不同的。

关于类型X的隐含性应转到X的伴随对象。因此,如果这是AString之间的隐式转换,则应转到A的伴随对象。但是随后您遇到了.getSimpleName的问题。

常见方法是为伴随对象(如@MarioGalic advises)参数化父特征:

https://github.com/scala/scala/blob/2.12.x/src/library/scala/collection/generic/GenericCompanion.scala#L30

https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/typeclass.scala#L44

#L84 #L150 #L178

如果您不想将T设为类型参数,则可以使其成为类型成员

trait Companion {    
  type T <: A
  implicit def str(a: T): String = s"${this.getClass.getSimpleName}: %d" format a.n
}

class A(val n: Int)
object A extends Companion { 
  type T = A 
}

class B(val x: Int, y: Int) extends A(y)
object B extends Companion { 
  type T = B 
}

此外,您可以尝试覆盖隐式

trait Companion {
  implicit def str(a: A): String = s"${this.getClass.getSimpleName}: %d" format a.n
}

class A(val n: Int)
object A extends Companion 

class B(val x: Int, y: Int) extends A(y)
object B extends Companion {
  override implicit def str(a: A): String = super.str(a)
}

trait LowPriorityCompanion {
  implicit def str(a: A): String = s"${this.getClass.getSimpleName}: %d" format a.n
}

trait Companion extends LowPriorityCompanion {
  override implicit def str(a: A): String = super.str(a)
}

class A(val n: Int)
object A extends LowPriorityCompanion 

class B(val x: Int, y: Int) extends A(y)
object B extends Companion 

答案 1 :(得分:0)

尝试像这样对参数Companion进行参数设置:

  trait Companion[T <: A] {

    implicit def str(a: T): String =
      s"${this.getClass.getSimpleName}: %d" format a.n
  }

  class A(val n: Int)
  object A extends Companion[A] {}

  class B(val x: Int, y: Int) extends A(y)
  object B extends Companion[B] {}

  val b = new B(5, 2)
  val s: String = b
  println(s) // B$: 2