再谈类型匹配

考虑以下例子,我们试图将输入的List[Double] 和List[String] 区分开:

// src/main/scala/progscala2/patternmatching/match-types.sc scala> for {
| x <- Seq(List(5.5,5.6,5.7), List("a", "b"))
| } yield (x match {
| case seqd: Seq[Double] => ("seq double", seqd)
| case seqs: Seq[String] => ("seq string", seqs)
| case _ => ("unknown!", x)
| })
<console>:12: warning: non-variable type argument Double in type pattern
Seq[Double] (the underlying of Seq[Double]) is unchecked since it is
eliminated by erasure case seqd: Seq[Double] => ("seq double", seqd)
<console>:13: warning: non-variable type argument String in type pattern 模式匹配 | 105
Seq[String] (the underlying of Seq[String]) is unchecked since it is
eliminated by erasure case seqs: Seq[String] => ("seq string", seqs)
<console>:13: warning: unreachable code case seqs: Seq[String] => ("seq string", seqs)
res0: List[(String, List[Any])] = List((seq double,List(5.5, 5.6, 5.7)),(seq double,List(a, b)))
这些警告表示什么?

Scala 运行于 JVM 中,这些警告来源于 JVM 的类型擦除,类型擦除是Java 5引入泛型后的一个历史遗留。为了避免与旧版本代码断代,JVM的字节码不会记住一个泛型实例(如List)中实际传入的类型参数的信息。所以,当编译器只能识别输入对象为 List,但无法在运行时识别它是 List[Double] 还是List[String]时,编译器就会发出警告。事实上,编译器认为第二个匹配List[String] 的case子句是不可达代码,意味着第一个匹配List[Double] 的 case 子句可以匹配任意List。输出显示,对于两个输入,都打印出了 seq double。
一个不太美观但却有效的解决方法是:

首先匹配集合,然后用一个嵌套的匹配语句去匹配集合中的第一个元素,从而决定其类型。这样的话,我们也就必须单独处理空序列:

// src/main/scala/progscala2/patternmatching/match-types2.sc 
def doSeqMatch[T](seq: Seq[T]): String = seq match { 
  case Nil => "Nothing" 
  case head +: _ => head match { 
  case _ : Double => "Double" 
  case _ : String => "String" 
  case _ => "Unmatched seq element" 
} 
} 

for { x <- 
    Seq(List(5.5,5.6,5.7), List("a", "b"), Nil) } yield {   x match {     case seq: Seq[_] => (s"seq ${doSeqMatch(seq)}", seq)     case _ => ("unknown!", x)     } }

以上脚本返回了期望的输出:
Seq((seq Double,List(5.5, 5.6, 5.7)), (seq String,List(a,b)), (seq Nothing,List()))。

posted @ 2022-08-02 18:04  linbo.yang  阅读(41)  评论(0编辑  收藏  举报