【问题标题】:Scala type erasure [duplicate]Scala类型擦除[重复]
【发布时间】:2020-07-27 21:02:10
【问题描述】:

我只是想在下面的示例中检查任何类型 T 的出现。尽管(try me) 断言失败:

val seq = List(AcceptableRisk, UnacceptableRisk)
def any[T] = 
    seq.collect { case x: T => x }.nonEmpty 

assert(any[UnknownRisk] == false) 

trait Risk
case class AcceptableRisk() extends Risk 
case class UnacceptableRisk() extends Risk 
case class UnknownRisk() extends Risk

有编译警告:

jdoodle.scala:5: warning: abstract type pattern T is unchecked since
it is eliminated by erasure
   seq.collect { case x: T => x }.nonEmpty
                         ^

为什么会失败?

【问题讨论】:

  • 这通常是代码异味,表明某处存在设计错误。
  • 是否有任何推荐的方法来检查异构集合中的类型出现?我不想在已知 T 的多个地方直接调用 collectnonEmpty
  • 一开始就没有异构集合。如何解决这个问题并没有直接简单的答案,但是每次有人说我有一个异构集合并且我需要提取一些任意类型时,这意味着之前的某个地方存在设计错误。
  • 你能给我指出任何一种关于这方面的文献吗?集合内容上的类型驱动模式匹配有什么问题?我敢打赌,项目暴露特定特征以进行过滤和相应处理是完全可以的。我们只让屏幕上的某些控件可以被鼠标点击,这并没有违反 LSP。
  • 任何异构都是 List[Any] 或接近的,这在 Scala 中是不好的做法,因为你真的不能用它们做那么多。由于擦除,按类型匹配的模式并不总是有效。例如,如果您将List[String] 作为T 传递,它仍将匹配内部List[Int]List[Boolean] 以及任何类型的列表。现在,如果你的T 只能是Risk 的子类型,那么这一切都没有任何意义,只是seq.exist(_ == UnacceptableRisk)

标签: scala scala-2.13


【解决方案1】:

有帮助:

def any[T <: Risk: Manifest] = 
    seq.collect { case x: T => x }.nonEmpty 

【讨论】:

  • 使用 ClassTag 而不是 Manifest。
【解决方案2】:

由于您的域是有限的,我会选择这样的:

trait Risk extends Product with Serializable
object Risk {
  def isUnacceptableRisk(risk: Risk): Boolean = risk match {
    case UnacceptableRisk(_) => true
    case _                   => false
  }
  // Repeat for other types if needed.
}

final case class AcceptableRisk(name: String) extends Risk 
final case class UnacceptableRisk(name: String) extends Risk 
final case class UnknownRisk(name: String) extends Risk

那么你可以:seq.exists(Risk.isUnacceptableRisk)

可以使用 typeclasses 完成更通用的解决方案,如下所示:

trait Is[T] {
  type U
  def check(t: T): Boolean
}

object Is {
  type Aux[T, _U] = Is[T] { type U = _U }
}

object syntax {
  object is {
     implicit class SeqOps[T](private val seq: Seq[T]) extends AnyVal {
       def any[U <: T](implicit ev: Is.Aux[T, U]): Boolean =
         seq.exists(ev.check)
     }
  } 
}

trait Risk extends Product with Serializable
object Risk {
  implicit final val IsUnacceptableRisk: Is.Aux[Risk, UnacceptableRisk] =
    new Is[Risk] {
      override type U = UnacceptableRisk
      override def check(risk: Risk): Boolean = risk match {
        case UnacceptableRisk(_) => true
        case _                   => false
      }
    }
    // Repeat for other types if needed.
}

final case class AcceptableRisk(name: String) extends Risk 
final case class UnacceptableRisk(name: String) extends Risk 
final case class UnknownRisk(name: String) extends Risk

import syntax.is._
List(AcceptableRisk("foo"), UnacceptableRisk("bar")).any[UnacceptableRisk] // true.

但是恕我直言,如果没有真正赢得太多,那就太辛苦了。

【讨论】:

  • 为什么需要在类型类Is中输入U?其实你不用它。
  • 我看不出与gist.github.com/DmytroMitin/87f2748d650d87e7882ffbf6e3eb82b4有任何有用的区别
  • @DmytroMitin 如果您想检查是否存在任何其他类型的风险怎么办?我的想法是 T 表示集合的上层类型,U 是您要搜索的特定类型。无论如何,正如我所说,我对Is 并不满意,这感觉就像太多样板而收获太少。
猜你喜欢
  • 2012-08-12
  • 2012-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-30
  • 2023-04-07
相关资源
最近更新 更多