Kar*_*ldt 1 generics casting scala covariance
我正在尝试为具有协变类型参数的类编写安全转换,例如:
case class Foo[+A](a: A) {
def safeCast[B <: A](): Option[B] = ???
}
Run Code Online (Sandbox Code Playgroud)
它由动态外部数据源填充,但我至少想静态地确保它B
是 的子类A
,然后None
在类型转换失败时返回 a 。我不断收到关于A
处于协变位置的错误。我可以得到它通过不使用的要进行类型检查<: A
,但我怎么能指定有关此静态保证B
?
我知道它会阻止诸如将 a 分配Foo[Dog]
给Foo[Animal]
val 之类的情况,然后尝试执行诸如safeCast[Mammal]
whereMammal
不是Dog
. 就我而言,这实际上没问题。即使允许投射到超类型 ofAnimal
也可以。我主要想静态地防止有人尝试safeCast[Plant]
.
请注意,我可以使用类外部的函数对其进行类型检查,如下所示,但我想要类上的方法。
def safeCast[A, B <: A](foo: Foo[A]): Option[B] = ???
Run Code Online (Sandbox Code Playgroud)
作为奖励,如果您知道使用猫或不使用 的东西来实现这一点的方法isInstanceOf
,那将非常有用。
类型类方法怎么样。
import scala.reflect.ClassTag
@annotation.implicitNotFound("${B} is not a subtype of ${A}")
sealed trait Caster[A, B] {
def safeCast(a: A): Option[B]
}
object Caster {
implicit def subtypeCaster[A, B](implicit ev: B <:< A, ct: ClassTag[B]): Caster[A, B] =
new Caster[A, B] {
override final def safeCast(a: A): Option[B] =
ct.unapply(a)
}
}
Run Code Online (Sandbox Code Playgroud)
可以这样使用:
sealed trait Animal
final case class Dog(name: String) extends Animal
final case class Cat(name: String) extends Animal
final case class Foo[+A](a: A)
implicit class FooOps[A] (private val foo: Foo[A]) extends AnyVal {
@inline
final def safeCastAs[B](implicit caster: Caster[A, B]): Option[Foo[B]] =
caster.safeCast(foo.a).map(b => Foo(b))
}
val animal: Foo[Animal] = Foo(Dog("luis"))
animal.safeCastAs[Dog]
// res: Option[Foo[Dog]] = Some(Foo(Dog("luis")))
animal.safeCastAs[Cat]
// res: Option[Foo[Cat]] = None
animal.safeCastAs[String]
// compile error: String is not a subtype of Animal
Run Code Online (Sandbox Code Playgroud)
两个重要的注意事项:
这里的技巧是将类外的方法定义为扩展方法。
(在这一点上,人们甚至可以考虑完全删除类型类,而只在扩展方法上使用隐式证据和类标签本身)。
由于使用了 classtag,您应该注意A和B都只是普通类型。如果它们是像List这样更高级的类型,您可能最终会遇到运行时错误。
归档时间: |
|
查看次数: |
103 次 |
最近记录: |