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

时间:2017-06-15 22:30:52

标签: 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

这就是错误:

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 {

由于thisthat的类型为Foo,而Foo只能是Foo_1Foo_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
}

我们收到以下编译器警告

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

3 个答案:

答案 0 :(得分:4)

Scala编译器不会为非密封特征(例如您的productFlavors { blue { flavorDimension "color" ext.squareId = "yourAppId" ext.circleId = "yourAppId" } android.applicationVariants.all { variant -> def flavors = variant.getFlavors() if (flavors[0].name.equals("square")){ variant.mergedFlavor.setApplicationId(flavors[1].ext.squareId) } ... } )提供详尽的匹配警告。这解释了为什么FoofooThis在没有警告的情况下编译。

如果您想要警告(并且您应该,因为它们在运行时优于fooThat例外),您有几个选择:

  1. 密封MatchError。这会创建一个ADT,这样可以安全地进行模式匹配,因为当您忘记一个案例时,您将获得详尽的警告。 Foo是您可能从标准库中熟悉的ADT。在这里,您已经有OptionFoo_1的案例,因此您无法获得详尽的警告。但如果你忘了这两种情况,你会的。您可能希望在Foo_2Foo_1决赛时进行最终决定。
  2. 保持Foo_2未密封,使用Typelevel Scala并启用其Foo警告。
  3. 另一方面,Scala编译器-Xlint:strict-unsealed-patmat等最终案例类提供详尽的匹配警告,这是您在Tuple2中匹配的内容。方法。

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

    foo

    (答案:它在运行时抛出case class Foo3() extends Foo val foo3 = Foo3() foo3.foo(foo3) 。)

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

    1. 密封MatchError(再次创建ADT),防止Foo潜入其他地方。
    2. 添加通配符Foo3
    3. 取消选中匹配项:case _ => ...
    4. 不要做第3,因为当有人介绍((this, that): @unchecked) match { ...时,它会让您在运行时容易受到MatchError的攻击。<​​/ p>

      所以,也许问题不是真的&#34;为什么Foo3中的匹配会产生警告&#34;但是&#34;为什么不会< / strong> foofooThis中的匹配会生成警告&#34;。

答案 1 :(得分:1)

似乎使抽象类密封至少会使编译器警告消失:

sealed abstract class Foo {

虽然我不太清楚为什么。它可能与:https://issues.scala-lang.org/browse/SI-9351

有关

答案 2 :(得分:0)

确定类的所有子类称为类层次结构分析,在动态代码加载的语言中执行静态CHA相当于解决暂停问题

另外,Scala的目标之一是独立编译和部署独立模块,因此编译器根本无法知道某个类是否在另一个模块中被子类化,因为它永远不会查看多个模块。 (毕竟,你可以在一个其他模块的接口上编译一个模块,而你的系统上甚至不存在该模块!)这就是为什么sealed需要在同一个编译单元中定义所有子类。 这就是编译器不显示警告的原因,因为它知道现有的子类。