Scala:使用协变 Field[+T] 访问属性
Scala: accessing properties using covariant Field[+T]
我正在使用一个自定义的不可变实体 class,它使用的键是扩展 Field[+T] 的 case 对象,其中 T 是协变的。这是有道理的,因为 Field[Stream[Int]] 应该能够被视为 Field[Seq[Int]]。
object ProblemWithCovariant {
sealed trait Field[+T]
case object _username extends Field[String]
case object _email extends Field[String]
case object _age extends Field[Byte]
case class Entity(attrs: Map[Field[Any], Any]) {
def apply[T](field: Field[T]): T = attrs(field).asInstanceOf[T]
def set[T](field: Field[T])(value: T): Entity = copy(attrs = attrs.updated(field, value))
}
def main(args: Array[String]): Unit = {
val user = Entity(Map.empty)
.set(_username)("John")
.set(_age)(23)
val username: String = user(_username) // alright
val ageInt: Int = user(_age) // compile error
}
}
然而,当我尝试检索 Byte 值并将其分配给 Int 字段时,这会导致编译错误。我知道 Byte 不是 Int 的子类型,它们唯一的关系是隐式转换。令我惊讶的是,在上面的示例中没有调用此转换,编译器现在要求我传递一个 Field[Int]!
经过一段时间的试验,我意识到我可以简单地明确提供类型,例如 val ageInt: Int = user[Byte](_age)
。虽然这有效,但它使我的代码比我希望的更冗长。有什么办法可以更改 Entity.apply
的签名,使其符合 field
参数的类型?
def apply[T, U](field: Field[U])(implicit ev: U => T): T =
ev(attrs(field).asInstanceOf[U])
有效。
我正在使用一个自定义的不可变实体 class,它使用的键是扩展 Field[+T] 的 case 对象,其中 T 是协变的。这是有道理的,因为 Field[Stream[Int]] 应该能够被视为 Field[Seq[Int]]。
object ProblemWithCovariant {
sealed trait Field[+T]
case object _username extends Field[String]
case object _email extends Field[String]
case object _age extends Field[Byte]
case class Entity(attrs: Map[Field[Any], Any]) {
def apply[T](field: Field[T]): T = attrs(field).asInstanceOf[T]
def set[T](field: Field[T])(value: T): Entity = copy(attrs = attrs.updated(field, value))
}
def main(args: Array[String]): Unit = {
val user = Entity(Map.empty)
.set(_username)("John")
.set(_age)(23)
val username: String = user(_username) // alright
val ageInt: Int = user(_age) // compile error
}
}
然而,当我尝试检索 Byte 值并将其分配给 Int 字段时,这会导致编译错误。我知道 Byte 不是 Int 的子类型,它们唯一的关系是隐式转换。令我惊讶的是,在上面的示例中没有调用此转换,编译器现在要求我传递一个 Field[Int]!
经过一段时间的试验,我意识到我可以简单地明确提供类型,例如 val ageInt: Int = user[Byte](_age)
。虽然这有效,但它使我的代码比我希望的更冗长。有什么办法可以更改 Entity.apply
的签名,使其符合 field
参数的类型?
def apply[T, U](field: Field[U])(implicit ev: U => T): T =
ev(attrs(field).asInstanceOf[U])
有效。