匹配可能不是详尽的警告是不正确的

Did*_*dii 4 scala pattern-matching compiler-warnings

所以scala 编译器抱怨模式匹配对方法来说可能并不详尽foo,我想知道为什么。这是代码:

abstract class Foo {
    def foo(that: Foo): Unit = (this, that) match {
        case (Foo_1(), Foo_1()) => //case 1
        case (Foo_1(), Foo_2()) => //case 2
        case (Foo_2(), Foo_1()) => //case 3
        case (Foo_2(), Foo_2()) => //case 4
            // Compiler warning
    }

    def fooThis(): Unit = this match {
        case Foo_1() => //do something
        case Foo_2() => //do something
            // Works fine
    }

    def fooThat(that: Foo): Unit = that match {
        case Foo_1() => //do something
        case Foo_2() => //do something
            // Works fine
    }
}
case class Foo_1() extends Foo
case class Foo_2() extends Foo
Run Code Online (Sandbox Code Playgroud)

这是错误:

Warning:(5, 32) match may not be exhaustive.
It would fail on the following inputs: (Foo(), _), (Foo_1(), _), (Foo_2(), _), (_, Foo()), (_, Foo_1()), (_, Foo_2()), (_, _)
    def foo(that: Foo): Unit = (this, that) match {
Run Code Online (Sandbox Code Playgroud)

由于thisandthat是 type Foo,并且Foo只能是Foo_1or类型Foo_2,所以 中的情况foo都是可能的组合。

我加fooThisfooThat为完整起见,并表明匹配Foo_1Foo_2足够了。编译器消息表明还有其他类型可以匹配(即Foo_)。

那么为什么会显示此警告?

有关的:


编辑

一旦您使用元组,编译器似乎就会抱怨。如果我们添加一个虚拟变量fooThis如下

def fooThis(): Unit = (this, Foo_1()) match {
    case (Foo_1(),_) => //do something
    case (Foo_2(),_) => //do something
}
Run Code Online (Sandbox Code Playgroud)

我们收到以下编译器警告

Warning:(13, 27) match may not be exhaustive.
It would fail on the following input: (_, _)
    def fooThis(): Unit = (this, Foo_1()) match {
Run Code Online (Sandbox Code Playgroud)

dan*_*xon 5

Scala 编译器不会对非密封特征(如您的Foo)给出详尽的匹配警告。这解释了为什么fooThis并且fooThat编译时没有警告。

如果你想在这里发出警告(你应该这样做,因为它们比MatchError运行时的异常要好)你有几个选择:

  1. Foo密封。这将创建一个ADT,它可以安全地进行模式匹配,因为当你忘记一个案例时你会得到详尽的警告。Option是您可能熟悉的标准库中的 ADT。在这里,您已经有了Foo_1and 的情况Foo_2,因此您不会收到详尽警告。但是,如果您忘记任何一种情况,您就会忘记。您可能想要制作Foo_1Foo_2最终完成。
  2. 保持Foo未密封状态,使用Typelevel Scala并启用其-Xlint:strict-unsealed-patmat警告。

另一方面,Scala 编译器将为最终的 case 类提供详尽的匹配警告,例如Tuple2,这是您在foo方法中匹配的内容。

要回答“为什么显示警告?”,请考虑如果我们这样做会发生什么:

case class Foo3() extends Foo
val foo3 = Foo3()
foo3.foo(foo3)
Run Code Online (Sandbox Code Playgroud)

(答案:它MatchError在运行时抛出 a 。)

警告是 Scala 编译器帮助您在运行时避免异常的方式。如果您想让警告消失,您可以:

  1. Foo密封(再次,创建ADT),防止Foo3在别处鬼鬼祟祟。
  2. 添加通配符case _ => ...
  3. 取消选中匹配项:((this, that): @unchecked) match { ...

不要做第 3 条,因为MatchError当有人引入Foo3.

因此,也许问题并不是真正的“为什么匹配中会foo生成警告”,而是“为什么匹配中fooThisfooThat生成警告”。