Scala模式匹配在元组中返回null时引发匹配错误

Scala模式匹配在元组中返回null时引发匹配错误,scala,pattern-matching,Scala,Pattern Matching,下面是两个片段,我无法理解为什么一个成功执行,而另一个抛出运行时异常 第1段: val str = "HELP" val perfectTuple: (String, String) = str match { case "NO-HELP" => ("First Help", "Second Help") case "OTHER-HELP" => ("I won't Help!", "Even,I won't Help!") case "HELP" =>

下面是两个片段,我无法理解为什么一个成功执行,而另一个抛出运行时异常

第1段:

val str = "HELP"

val perfectTuple: (String, String) = str match {
    case "NO-HELP" => ("First Help", "Second Help")
    case "OTHER-HELP" => ("I won't Help!", "Even,I won't Help!")
    case "HELP" => (null,"Single Help")
    case _ => throw new NoSuchMethodException

  }
第2段:

val str = "HELP"

val (firstPart:String, secondPart:String) = str match {
  case "NO-HELP" => ("First Help", "Second Help")
  case "OTHER-HELP" => ("I won't Help!", "Even,I won't Help!")
  case "HELP" => (null,"Single Help")
  case _ => throw new NoSuchMethodException
}
==========================

这两个片段之间的差别很小。其中一个将返回的元组存储到类型为tuple2的值“perfectTuple”中,并成功执行该元组

另一个从元组2中提取值并将其存储到字符串值中,并抛出运行时“scala.matchError”

这是scala中的一个bug吗

我已经在scala 2.10.5和2.11.7上试过了

提前谢谢

=============

还有一个场景,在这个场景中,我可以从模式匹配中将null分配给字符串,而这个场景非常完美: 第3段:

val str = "HELP"

val assignNullToString: String = str match {

    case "NO-HELP" => "ONE"
    case "OTHER-HELP" => "TWO"
    case "HELP" => null
    case _ => throw new NoSuchMethodException
}          
所以我假设,并不是我给字符串赋值null导致了问题,而是元组的问题?代码段2有什么问题,而代码段1运行得非常好。

第三种模式
的“帮助”
是匹配的,但错误是因为您试图将
null
分配给
第一部分
,它需要一个
字符串。您可以在下面的小示例中看到类似的错误。您的代码的行为类似

scala> val (firstPart: String, secondPart: String) = ("foo", "bar")
firstPart: String = foo
secondPart: String = bar

scala> val (firstPart: String, secondPart: String) = (null, "bar")
<console>:10: error: pattern type is incompatible with expected type;
 found   : String
 required: Null
       val (firstPart: String, secondPart: String) = (null, "bar")
scala>val(第一部分:字符串,第二部分:字符串)=(“foo”,“bar”)
第一部分:String=foo
第二部分:字符串=条
scala>val(第一部分:字符串,第二部分:字符串)=(空,“条”)
:10:错误:图案类型与预期类型不兼容;
找到:字符串
必需:空
val(第一部分:字符串,第二部分:字符串)=(空,“条”)

如果将元组第一部分中的
null
更改为
字符串
,则可以正常工作。

第二个示例使用了
案例类Tuple2的

现在,我还没有看过优化后的
Tuple2的代码。不应用
,但我猜它在某个点上对tuple的值进行了类型匹配

而且不能在
null
上键入匹配

val str: Any = null

str match {
  case _: String => "yay"
  case other => "damn"
}
|-> res1: String = damn
更新

让我们把你的第二个例子略作删节:

val str = "HELP"

val (firstPart:String, secondPart:String) = str match {
  case "NO-HELP" => ("First Help", "Second Help")
  case "OTHER-HELP" => ("I won't Help!", "Even,I won't Help!")
  case "HELP" => (null,"Single Help")
  case _ => throw new NoSuchMethodException
}
提取匹配时,我们得到:

val tuple: (String, String) = str match {
  case "NO-HELP" => ("First Help", "Second Help")
  case "OTHER-HELP" => ("I won't Help!", "Even,I won't Help!")
  case "HELP" => (null,"Single Help")
  case _ => throw new NoSuchMethodException
}
现在,将其放入
Tuple2
函数中。看看签名:

def unapply[A, B](tuple: Tuple2[_, _]): Option[(A, B)]
所以传入元组的值上的类型被删除了! 然而,当你说

val (first: String, second: String) = tuple
您正在调用
Tuple2。使用类型参数
[String,String]
取消应用
,明确要求结果为
(String,String)

要能够返回
选项[(字符串,字符串)]
,unapply函数必须键入与这两个值匹配的值

人们可以想象Tuple2
伴生对象看起来像什么,但实际上更高效、更复杂:

object Tuple2 {
  def apply[A, B](_1: A, _2: B): Tuple2[A, B] = new Tuple2(_1, _2)

def unapply[A, B](tuple: Tuple2[_, _]): Option[(A, B)] = {
  val a: Option[A] = tuple._1 match { case a: A => Some(a) }
  val b: Option[B] = tuple._2 match { case b: B => Some(b) }

  a.zip(b).headOption
}
下面这行代码将抛出MatchError:

  val a: Option[A] = tuple._1 match { case a: A => Some(a) }
正如我前面所说,我没有研究过Tuple2的优化代码,但我有理由相信这与实际情况非常接近

当然,您可以随时放松您的要求:

val str = "HELP"

val (firstPart, secondPart) = str match {
  case "NO-HELP" => ("First Help", "Second Help")
  case "OTHER-HELP" => ("I won't Help!", "Even,I won't Help!")
  case "HELP" => (null,"Single Help")
  case _ => throw new NoSuchMethodException
}
在大多数情况下都能很好地发挥作用,但可能只会延缓疼痛

编辑2

我建议您始终注意这样一个事实,即当使用提取器绑定VAL时,使用语法糖进行模式匹配,因为模式匹配可能总是会因匹配错误而失败

这里有一些明显的例子,我见过几次以不太明显的方式发生

val foo = ("lorem", 2)
val (lorem: String, bad: String) = foo // fail 

case class Bar(name: String, age: Option[Int])
val bar = Bar("Sam", None)
val Bar(name, Some(age)) = bar // fail

这并不能真正解释在这种情况下发生了什么-
val foo:String=null
很好。请尝试
val(a:String,b:String)=(null.asInstanceOf[String],“b”)
感谢Brian的回复。我还包括了另外一个场景,它从模式匹配中将null分配给字符串。所以我确信赋值null不是这里真正的问题。@m-z我得到了一个
scala.MatchError
val(a:Option[String],b:Option[String])=(None,Some(“b”))
是否更可取?@Tarun将null赋值给元组的元素和值的行为与您发现的不同。下面的Tuple2案例类定义声明了协变T1和T2…:案例类Tuple2[+T1,+T2](_1:T1,_2:T2)扩展了Product2[T1,T2]对于可序列化的产品,这意味着它应该接受字符串类型下的null对象,并且应该可以吗?@Tarun您可以根据需要传递
null
。但是提取器并不是简单地使用
asInstanceOf
来返回请求的值类型。这并不完全是类型安全的。相反,(通常)类型匹配用于检查类型是否匹配。我将把我的答案扩大一点。。。您的解释说明了为什么null会导致scala抛出匹配错误。。。谢谢:)但是,如果我可以冒昧地进一步扩展我的问题,“scala让开发人员对代码段1和代码段2都期望相同的结果,这不是最好的做法吗?因为两个代码段的R.H.S都是相同的,并且它们的L.H.S.或多或少是从R.H.S.中提取值的语法糖。”@Tarun我不会将代码片段2中的用法称为“提取从R.H.S中获得的值的或多或少的语法糖”。参见我的答案第2部分