在类型层次结构中,从参数到返回类型的 Scala 类型多态性
Posted
技术标签:
【中文标题】在类型层次结构中,从参数到返回类型的 Scala 类型多态性【英文标题】:Scala type polymorphism from parameter to return type, within type hierarchy 【发布时间】:2015-07-10 01:03:30 【问题描述】:我有一些Collection[SuperType]
类型的集合。这个集合中存储了几个值,它们是SuperType
的子类型,我希望集合只允许自己包含每个子类型的一个实例(有点像集合,但不是)。
我正在尝试编写一个函数,当给定上述子类型之一的伴生对象时,它可以返回伴生对象所属类的第一个实例。
最初我尝试使用如下所示的 Set,但 T 会遭受类型擦除,因此模式匹配将失败。然后我也意识到 Set 不适合这个任务,因为我只希望集合中的每个子类型出现一次。
def get[T <: SuperType](target: T): Option[SuperType] =
collection.collectFirst(
case target: T => target
)
我的下一个也是当前的方法是使用映射,其中键是伴随对象,值是伴随对象类的实例。类型层次如下所示。
trait SuperType
trait SuperTypeValue
// Pretend this has some parameters
case class ExampleSubType extends SuperTypeValue
case object ExampleSubType extends SuperType
// value for use in later example
val uniqueToObjectField: String = "hello"
val collection: Map[SuperType, SuperTypeValue] = // Some new map
def get(target: SuperType): Option[SuperTypeValue] =
collection.get(target)
上面的效果很好。但是,我想保留用作参数的子类型的类型,并将其用作返回类型。我相信函数的签名看起来像这样:
get[T <: SuperType](target: T): Option[T]
// So I could then do something like this
get(ExampleSubType) match
case Some(exampleSubType) => exampleSubType.uniqueToObjectField
case _ => "nope"
这在scala中可能吗?如果是这样,怎么做?如果没有,这在其他语言中是否存在?它叫什么?
希望这个问题没有明显的问题,但现在是凌晨 2 点,所以我会在早上再检查一遍。
【问题讨论】:
难道不能简单地重新定义equals方法,如果两个实例相同则返回true,然后直接使用Set吗? 好主意,应该可以。不知道我对劫持 equals 方法的感觉如何。 【参考方案1】:您可以使用ClassTags
绕过类型擦除。与其使用伴生对象,不如直接提供泛型参数更容易:
import scala.reflect._
trait SuperType val x: Int
case class Foo(x: Int) extends SuperType
case class Bar(x: Int) extends SuperType
val collection = Set(Foo(1), Foo(2), Bar(3), Foo(4), Bar(5))
def get[T <: SuperType : ClassTag]: Option[T] =
collection.collectFirst
case target: T => target
然后你可以调用:
get[Foo] //Foo(1)
get[Bar] //Bar(3)
【讨论】:
谢谢,很高兴我可以使用类型参数!我已将def get[T <: SuperType : ClassTag]: Option[SuperType]
更改为 def get[T <: SuperType : ClassTag]: Option[T]
,因此我可以访问可能未由 SuperType
定义的成员,并且对于子类型是唯一的。
哦,那当然好多了!我已经更新了答案。【参考方案2】:
当您需要锯子时,您会尝试使用锤子。您应该为此创建一个新类,并为每种类型创建一个字段。
class SomeClass
a:TypeA
b:TypeB
c:TypeC
// if c extends b extends a maybe you want
// to prevent a TypeC being assigned to A I have no idea
// you can change these implementations to get the behavior you want
addA(a:TypeA) this.a = a
addB(b:TypeB) this.b = b
addC(c:TypeC) this.c = c
新手经常会出于疯狂的目的尝试使用集合。仅仅因为一个集合包含数据,并不意味着您想在任何时候都需要一个数据。在决定要使用什么之前,您需要首先考虑您的需求是什么,而不是反过来,如果您采用这种方法,您将在余下的编程生命中使用 SO。
【讨论】:
我应该在我的问题中明确说明这一点,但我无法明确知道所有子类型。如果某个库的用户想要为自己的自定义子类型扩展超类型怎么办?所以我想用你的锯子,但锤子更适合我的用例。以上是关于在类型层次结构中,从参数到返回类型的 Scala 类型多态性的主要内容,如果未能解决你的问题,请参考以下文章
如何创建类型化工厂方法构造函数的类层次结构并使用抽象类型从 Scala 访问它们?