在此Scala模式匹配中未经检查的类型参数警告?

13

这个文件:

object Test extends App {
    val obj = List(1,2,3) : Object
    val res = obj match {
       case Seq(1,2,3) => "first"
       case _ => "other"
    }
    println(res)
}

会产生以下警告:

Test.scala:6: warning: non variable type-argument A in type pattern Seq[A]  
is unchecked since it is eliminated by erasure
   case Seq(1,2,3) => "first"

Scala版本2.9.0.1。

我不明白为什么需要擦除类型参数来执行匹配。第一个case子句的目的是询问obj是否是一个包含3个元素1、2和3的Seq。

如果我写了类似于以下内容,我就能理解这个警告:

case strings : Seq[String] => ...

为什么我会收到警告,有什么好的方法可以消除它呢?顺便说一句,我确实想匹配静态类型为Object的东西。在我的实际代码中,我正在解析类似于Lisp数据的东西 - 它可能是字符串、数据序列、符号、数字等。


有趣的是,在这种情况下,Scala版本2.8.1不会给出此警告。 - thoredge
@thoredge,我猜2.9.0版本中的警告可能被认为是一种改进。我不知道这种情况是否被预见到了。 - huynhjl
2个回答

6

以下是幕后发生的一些情况。请考虑以下代码:

class Test {
  new Object match { case x: Seq[Int] => true }
  new Object match { case Seq(1) => true }
}

如果你使用 scalac -Xprint:12 -unchecked 进行编译,你会看到在类型擦除阶段之前(id 13)的代码。对于第一个 类型模式,你会看到类似以下内容:
<synthetic> val temp1: java.lang.Object = new java.lang.Object();
if (temp1.isInstanceOf[Seq[Int]]()) 

对于“Seq提取器模式”,您将看到类似以下内容的东西:
<synthetic> val temp3: java.lang.Object = new java.lang.Object();
if (temp3.isInstanceOf[Seq[A]]()) {
  <synthetic> val temp4: Seq[A] = temp3.asInstanceOf[Seq[A]]();
  <synthetic> val temp5: Some[Seq[A]] = collection.this.Seq.unapplySeq[A](temp4);
  // ...
}

在这两种情况下,都会有一种类型测试来查看对象是否为Seq类型(Seq[Int]Seq[A])。类型参数将在消除阶段中消除。因此才会出现警告。即使第二种可能是意外的情况,检查类型仍然是有意义的,因为如果对象不是Seq类型,则该子句将不匹配,JVM可以继续处理下一个子句。如果类型匹配,则可以将对象转换为Seq并调用unapplySeq函数。
关于thoredge对类型检查的评论,也许我们正在谈论不同的事情。我只是在说:
(o: Object) match {
  case Seq(i) => println("seq " + i)
  case Array(i) => println("array " + i)
}

翻译成中文大概是这样:

if (o.isInstanceOf[Seq[_]]) { // type check
  val temp1 = o.asInstanceOf[Seq[_]] // cast
  // verify that temp1 is of length 1 and println("seq " + temp1(0))
} else if (o.isInstanceOf[Array[_]]) { // type check
  val temp1 = o.asInstanceOf[Array[_]] // cast
  // verify that temp1 is of length 1 and println("array " + temp1(0))
}

类型检查用于确保在执行转换操作时不会出现类转换异常。

关于警告“非变量类型参数A在类型模式Seq[A]中未经检查,因为它会被擦除”是否合理,以及在进行类型检查的情况下是否仍可能出现类转换异常,我不清楚

编辑:这里是一个示例:

object SeqSumIs10 {
  def unapply(seq: Seq[Int]) = if (seq.sum == 10) Some(seq) else None
}

(Seq("a"): Object) match {
  case SeqSumIs10(seq) => println("seq.sum is 10 " + seq) 
}
// ClassCastException: java.lang.String cannot be cast to java.lang.Integer

1
那这是一个bug吗?在这种情况下,类型检查只是一种优化。 - thoredge
@thoredge,你不会得到ClassCastException,因为编译器添加了isInstanceOf检查。 - huynhjl
@Rob N,isInstanceOf检查应该在unapplySeq调用之前(可能是几行之前)。 - huynhjl
@huynhjl,我有点困惑。如果编译器发出警告,表示模式Seq[A]未经A检查,则我们应该在某些情况下看到ClassCastException;对吗? - thoredge
我仍然无法理解以下几点:
  1. 这些警告是在警示哪个问题或潜在问题?
  2. 如果类型已经被检查,为什么还会标记为“unchecked”?
  3. 如何重写代码以消除警告(及其所指示的问题)?
- Ivan
显示剩余5条评论

4

将匹配对象声明在外部至少可以使其消失,但我不确定为什么:

class App
  object Test extends App {
  val obj = List(1,2,3) : Object
  val MatchMe = Seq(1,2,3)
  val res = obj match {
    case MatchMe => "first"
    case _ => "other"
  }
  println(res)
}

2
是的,找得好。这是因为在这种情况下,它使用了一个“稳定标识符模式”,根据语言规范,这些模式只编译成相等性测试,不需要转换类型。但如果你想绑定变量,那就没办法了。 - huynhjl
很好知道,但是是的,我需要绑定变量。 - Rob N
1
如果我匹配一个类型(比如 Seq[Int]),则任何整数序列都被视为有效匹配,而不仅仅是(1、2、3)的情况怎么办? - Ivan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接