从超类列表中提取给定子类的元素
Posted
技术标签:
【中文标题】从超类列表中提取给定子类的元素【英文标题】:Extract element of given subclass from list of superclass 【发布时间】:2021-09-10 05:42:47 【问题描述】:鉴于此代码
class Animal
class Dog extends Animal
class Cat extends Animal
class Pitbull extends Dog
object MyClass
def main(args: Array[String])
val animals: List[Animal] = List(new Dog(), new Cat(), new Pitbull(), new Dog(), new Cat())
getElementOftype(animals, PitBull)
def getElementOftype(list: List[Animal], givenType: ???): Animal =
我想从这个 List 中提取 Pitbull 类型的第一个元素,我应该如何处理?
我试过但感觉不对
trait Identity
def who: String
class Animal extends Identity
override def who = "Animal"
class Dog extends Animal with Identity
override def who = "Dog"
class Cat extends Animal with Identity
override def who = "Cat"
class Pitbull extends Dog with Identity
override def who = "Pitbull"
object MyClass
def main(args: Array[String])
val animals: List[Animal] = List(new Dog(), new Cat(), new Pitbull(), new Dog(), new Cat())
println(getElementOftype(animals, new Pitbull()))
def getElementOftype(list: List[Animal], givenType: Animal): Option[Animal] =
list.collectFirstcase s if s.who == givenType.who => s
我想简单地将Pitbull
作为参数传递,而不是实例化一个空对象。
这个方法可行,但我认为这不是最好的方法。 必须有一种更大规模的方式来做到这一点。
【问题讨论】:
你尝试了什么,什么不起作用? @GaëlJ 我添加了我用作解决方法的代码。 一般来说,您应该避免出现这种情况。当绝对必要时ClassTag
在类型擦除没有问题的情况下工作,否则您可能需要更高级的东西,例如 Shapeless 或 TypeTag
或您自己的 typeclass.
【参考方案1】:
你可以这样做(未经测试,可能有一些错别字):
def getElementOftype[A <: Animal](list: List[Animal])(implicit ct: ClassTag[A]): Option[A] =
list.collectFirst case s: A => s
ClassTag
是匹配collectFirst
中A
类型所必需的。
【讨论】:
我应该使用getElementOftype(animals)(Pitbull)
这个函数?
getElementOftype[Pitbull](animals)
如果您愿意,也可以显式传递 Class
作为第二个参数,但这不是必需的。
谢谢,代码运行良好。我不得不使用scala.reflect
,我只是想知道这是否会对性能产生任何影响,因为我已经被警告过。我对反射不熟悉,所以我无法自己确定
我不是专家,但 ClassTag 在编译时提供。 AFAIK 不应该有运行时成本。【参考方案2】:
当您写 Animal extends Identity
时,您的意思是 Animal IS an Identity,这是错误的,最好使用这种方法:
class Animal self: Identity =>
这意味着当你想实例化一个 Animal 时,你需要给它一个 Identity,但你为什么要这样做呢? 您可以像这样简单地使用模式匹配来过滤所需的类型:
import scala.reflect.ClassTag
def getElementsOfType[A <: Animal : ClassTag](animals: List[Animal]): Option[A] =
animals.filter
case a: A => true
case _ => false
.map(_.asInstanceOf[A]).headOption
headOption 是因为你的 api 返回 Option[A] 作为结果,你可以让它返回 List[A] 并去掉 headOption,以防列表中有多个 Pitbull :)
【讨论】:
这行不通。了解类型擦除。 @sarveshseri 在线结果here! 因为第一个元素是Dog
。尝试将您的输入更改为val list: List[Animal] = List(new Cat())
。 Scastie 本身正在给你abstract type pattern A is unchecked since it is eliminated by erasure
警告。另外,您为什么要使用 filter
+ map
+ headOption
组合,即使在找到所需元素之后也会遍历列表,而且效率低下。
哦,是的!我忘记了类标签,我进行了编辑。感谢您的有用评论@sarveshseri!
最好使用find
或collectFirst
而不是filter + map + headOption
这个东西。以上是关于从超类列表中提取给定子类的元素的主要内容,如果未能解决你的问题,请参考以下文章