[英]How to override Scala generic methods for specific types?
我目前有这样的事情:
case class Bear(a: String, b: String) {
val can: Can[T] = ??
def drink[T](str: String) = can.open(str)
}
我需要将其修改为仅用于 4 种类型 A、B、C 和 D。例如,给定一个 Bear 实例,我们应该只能调用bearinstance.drink[A]("abc")
、 bearinstance.drink[B]("abc")
、 bearinstance.drink[C]("abc")
和bearinstance.drink[D]("abc")
。 不应允许任何其他类型。
现在的问题是如何为特定类型重写此方法?
另一个问题是can
,假设我设法将drink
重写为仅用于类型“A”、“B”、“C”和“D”,我将必须为所有四种类型创建can
作为成员变量。 如何根据类型动态创建通用方法 select can
? 一种选择是在 class 之外隐式声明can
,但它需要声明 class 参数。
任何线索将不胜感激。
您需要这样做的事实意味着您确实应该重构您的代码。
但无论如何...
尝试使用隐式参数:
case class Bear(a: String, b: String) {
val can: Can[T] = ???
def drink[T](str: String)(implicit ev: CanDrink[T]) = can.open(str)
}
然后使用隐式实例创建一个特征CanDrink
:
trait CanDrink[T]
implicit object ACanDrink extends CanDrink[A]
implicit object BCanDrink extends CanDrink[B]
//And so on
现在你可以这样称呼它:
bearinstance.drink[A]("abc")
//Gets implicit object ACanDrink
bearinstance.drink[X]("abc")
//Doesn't work because no implicit parameter given of type CanDrink[X]
在 Dotty 中,您可以尝试使用联合类型更改饮料的定义,如 Dmytro Mitin 所建议的:
def drink(x: A | B | C | D)(str: String) = ???
def drink[T](str: String)(using T <:< (A | B | C | D)) = ???
如果需要动态确定,请使用ClassTag
。
def drink[T](str: String)(implicit ev: ClassTag[T]) = ev match {
case classOf[A] => ???
...
}
如果您实际上覆盖了泛型方法,则必须为所有可能的类型参数类型实现它(否则您违反了类的约定):
trait BearLike {
def drink[T](str: String)
}
case class Bear(a: String, b: String) extends BearLike {
override def drink[T](str: String) = ??? // for all T
}
或者
case class Bear(a: String, b: String) {
def drinkABCD[T](str: String)(implicit can: Can[T]) = can.open(str) // only for A, ..., D
}
或者
case class Bear(a: String, b: String) extends BearLike {
override def drink[T](str: String): Unit = sys.error("Bear is not actually a BearLike")
def drinkABCD[T](str: String)(implicit can: Can[T]) = can.open(str) // only for A, ..., D
}
只要有Can[A]
, ..., Can[D]
trait Can[T] {
def open(str: String)
}
object Can {
implicit val a: Can[A] = ???
implicit val b: Can[B] = ???
implicit val c: Can[C] = ???
implicit val d: Can[D] = ???
}
如果您可以修改合同,那么您可以将此限制(该方法仅适用于A
,..., D
)添加到合同中
trait BearLike {
def drink[T](str: String)(implicit can: Can[T])
}
case class Bear(a: String, b: String) extends BearLike {
override def drink[T](str: String)(implicit can: Can[T]) = can.open(str)
}
有时将 FP(类型类)与 OOP(继承)结合起来并不容易。 通常,如果您开始使用类型类( Can
),您应该更喜欢类型类
trait BearLike[B] {
def drink(str: String)
}
case class Bear[T](a: String, b: String)
object Bear {
implicit def bearIsBearLike[T](implicit can: Can[T]): BearLike[Bear[T]] = new BearLike[Bear[T]] {
override def drink(str: String): Unit = can.open(str)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.