Scala模式匹配在元组中返回null时引发匹配错误
下面是两个片段,我无法理解为什么一个成功执行,而另一个抛出运行时异常 第1段: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" =>
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部分