在类型层次结构中,从参数到返回类型的Scala类型多态性



我有一些类型为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点,所以我将在早上再检查一遍。

您可以使用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)

你明明需要锯子却想用锤子。您应该为此创建一个新类,并为每个类型创建一个字段。

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。

最新更新