【问题标题】: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.collectFirst{case s if s.who == givenType.who => s}
    }
}

我想简单地将Pitbull 作为参数传递,而不是实例化一个空对象。

这个方法可行,但我认为这不是最好的方法。 必须有一种更大规模的方式来做到这一点。

【问题讨论】:

  • 你尝试了什么,什么不起作用?
  • @GaëlJ 我添加了我用作解决方法的代码。
  • 一般来说,您应该避免出现这种情况。当绝对必要时 ClassTag 在类型擦除没有问题的情况下工作,否则您可能需要更高级的东西,例如 ShapelessTypeTag 或您自己的 typeclass.

标签: scala scala-collections


【解决方案1】:

你可以这样做(未经测试,可能有一些错别字):

def getElementOftype[A <: Animal](list: List[Animal])(implicit ct: ClassTag[A]): Option[A] = {
  list.collectFirst { case s: A => s }
}

ClassTag 是匹配collectFirstA 类型所必需的。

【讨论】:

  • 我应该使用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!
  • 最好使用findcollectFirst 而不是filter + map + headOption 这个东西。
猜你喜欢
  • 2013-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-03
  • 1970-01-01
  • 2020-03-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多