如何让 scala 编译器找到 case 类 used with wrong arguments

How to make the scala compiler find case classes used with wrong arguments

我有一个 case class Disconnect(nodeId: PublicKey),里面有 1 个参数,但是在代码的其他部分,它恰好在没有参数的情况下使用,即:Disconnect,编译器没有捕捉到错误,请注意,我还尝试使用 -Xlint 选项 运行 编译器,但它仍然无法捕获错误。

[history] ​​以前是case object Disconnect,后来改成case class,加了一个参数,代码中还是无参实例化,编译器无法注意它。我尝试向编译器添加 -Xlint 选项,但没有帮助。

在Peer.scala

  object Peer { 
    // other code
    case class Disconnect(nodeId: PublicKey)
    // more code
  }  

在Channel.scala

  // inside a function
  revocationTimeout.peer ! Peer.Disconnect
  //

我希望编译器能够捕捉到大小写的误用 class 而无法编译。

编辑:感谢大家的回复,确实编译器做得很好并且 Disconnect 被用作类型而不是 case class 实例,这是可能的,因为它被使用在接受 Any 作为参数的函数中。

问题不在编译器中,而是在接受 Any 作为参数的方法 ! 中:

def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit

所以我们可以将参数更改为任何内容,它仍然可以编译,例如,

revocationTimeout.peer ! "woohoo"  // compiles OK!

另一方面,如果我们查看相应的 Akka Typed ! 方法

implicit final class ActorRefOps[-T](val ref: ActorRef[T]) extends AnyVal {
    def !(msg: T): Unit = ref.tell(msg)
}

然后我们看到它是用类型参数 T 参数化的,编译器会捕获它。

我假设 ! 是来自 akka actor 的 tell 运算符。

它的签名是

def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit

所以您可以向它发送任何字面上的内容,在本例中您发送的是 Disconnect 类型。 这是使用 akka actor 的最大缺点之一,这就是为什么有一个新模块 akka typed,您可以在其中为 actor 定义 type-safe 行为。

您可能想知道,如果您发送的是您不希望的对象,为什么这不会在运行时爆炸。原因是 actor 的接收是一个 PartialFunction[Any, Unit],"discards" 没有为 PF 定义的消息。

由于您将 Disconnect 声明为 case class,编译器会自动生成一个伴随 object Disconnect,其中包含所有整齐的 applyunapply方法。因此,Peer.Disconnect 是单例类型 Peer.Disconnect.type 的完全有效表达式。有人可能会争辩说,如果您从一开始就使用 Akka Typed 就不会发生这种情况,但是在您的代码中, ! 方法接受任何东西,因此为了强制编译器发出一些有意义的错误消息,您需要别的东西。这是一种简单的方法:

  1. 恢复到 Disconnect 是单例对象的状态,没有关联的大小写 class。
  2. 完全删除 Disconnect 定义。添加 case class NewDisconnect 代替。现在每次出现 Peer.Disconnect 都会变成 正确的错误 .
  3. 将所有 Peer.Disconnect 替换为 Peer.NewDisconnect(foo)
  4. NewDisconnect 重命名为 Disconnect