【问题标题】:A way to avoid asInstanceOf in Scala在 Scala 中避免 asInstanceOf 的一种方法
【发布时间】:2019-08-11 14:44:36
【问题描述】:

我在 Scala 中有这种特征和类的层次结构:

trait A
trait B[T] extends A {
  def v: T
}
case class C(v:Int) extends B[Int]
case class D(v:String) extends B[String]
val l:List[A] = C(1) :: D("a") :: Nil
l.foreach(t => println(t.asInstanceOf[B[_]].v))

我无法更改类型层次结构或列表类型。

有没有更好的方法来避免 asInstanceOf[B[_]] 语句?

【问题讨论】:

  • 您能否从一开始就将列表l 声明为List[B[_]],而不是将其明确声明为List[A]
  • 如果您不能更改列表的类型,您如何确定所有实例都是B 类型?如果任何实例的类型不是B,会发生什么?忽略它并继续?异常失败?
  • 我无法更改列表的类型。如果某些东西不是 B 类型,请忽略。

标签: scala generics scala-collections


【解决方案1】:

您可以尝试模式匹配。

l.collect{case x :B[_] => println(x.v)}

【讨论】:

  • 如果该列表实际上不是 List[B[_]],则会以 MatchError 崩溃。
  • 好的...为什么现在要收集无用的()-values 列表?
  • 在扔掉它们之前,我让我的猫和它们玩耍。
【解决方案2】:

你可以试试这样的:

for (x <- l.view; y <- Some(x).collect { case b: B[_] => b }) println(y.v)

它不需要任何isInstanceOfasInstanceOf,并且永远不会崩溃,即使您的列表包含不是B[_]s 的As。它也不会创建任何冗长的列表作为中间结果,只会创建小的短暂的Options。


没有那么简洁,但也没有那么令人惊讶的解决方案:

for (x <- l) {
  x match {
    case b: B[_] => println(b.v)
    case _ => /* do nothing */
  }
}

如果您可以将l 的类型更改为List[B[_]],这将是更可取的解决方案。

【讨论】:

  • 如果upvote/unupvote是由于怀疑可反驳的模式b: B[_]是否可以在for中直接添加withFilter步骤:显然它不能:for (b: B[_] &lt;- l) { ... }不不编译。至少我不知道如何强制它编译。
【解决方案3】:

我认为最理想的方法是为B 提供一个提取器对象和B 值的模式匹配:

object B {
    def unapply[T](arg: B[T]): Some[T] =  Some(arg.v)
  }
l.collect{case B(x) => println(x)} 

如果 B 在源文件中声明,您无法更改,您可能需要为提取器对象使用不同的名称。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    • 2020-01-30
    • 1970-01-01
    • 2019-02-09
    • 2017-02-11
    • 1970-01-01
    相关资源
    最近更新 更多