我有一个包含 1 个参数的case class Disconnect(nodeId: PublicKey)
,但是在代码的其他部分,它碰巧在没有参数的情况下使用,即:Disconnect
并且编译器没有捕获错误,请注意,我也尝试使用-Xlint
选项运行编译器,但它仍然无法捕获错误。
- 斯卡拉版本:2.11.12
- 目标 JVM 版本:1.8
- 使用选项编译的代码:
-deprecation
-feature
-language:postfixOps
-language:implicitConversions
-Xfatal-warnings
-unchecked
-Xmax-classfile-name 140
-nobootcp
[历史] 它曾经是case object Disconnect
的,但在某个时候它已被更改为 case 类并添加了一个参数,在代码中它仍然是无参数实例化的,编译器无法注意到它。我尝试将-Xlint
选项添加到编译器中,但没有帮助。
在 Peer.scala 中
object Peer {
// other code
case class Disconnect(nodeId: PublicKey)
// more code
}
在 Channel.scala 中
// inside a function
revocationTimeout.peer ! Peer.Disconnect
//
我希望编译器会发现 case 类的误用并且无法编译。
编辑:感谢大家的回复,确实编译器做得很好,Disconnect
被用作类型而不是案例类实例,这是可能的,因为它用于接受Any
作为参数的函数。
由于你声明Disconnect
是一个case class
,编译器会自动生成一个配套object Disconnect
,其中包含所有简洁的apply
和unapply
方法。因此,Peer.Disconnect
是单例类型Peer.Disconnect.type
的完全有效表达式。有人可能会争辩说,如果你从一开始就使用 Akka Typed,就不会发生这种情况,但是在你的代码中,!
方法接受任何内容,所以为了强制编译器发出一些有意义的错误消息,你需要别的东西。下面是一个简单的方法:
- 还原到
Disconnect
是单一实例对象的状态,没有关联的事例类。 - 完全删除
Disconnect
定义。改为添加case class NewDisconnect
。现在,每次出现Peer.Disconnect
都会成为一个适当的错误。 - 将所有
Peer.Disconnect
替换为Peer.NewDisconnect(foo)
- 将
NewDisconnect
重命名为Disconnect
。
我假设!
是 akka 演员的 tell 运算符。
它的签名是
def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit
因此,您实际上可以向其发送任何内容,在这种情况下,您正在发送类型断开连接。 这是使用 akka actor 的最大缺点之一,这就是为什么有一个新模块 akka 类型化,您可以在其中为参与者定义类型安全的行为。
您可能想知道,如果您要发送一个您不希望的对象,为什么这在运行时不会爆炸。原因是参与者的接收是一个 PartFunction[Any, Unit],它"丢弃"未为 PF 定义的消息。
问题不在于编译器,而在于接受Any
作为参数的方法!
:
def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit
因此,我们可以将参数更改为任何内容,并且它仍然可以编译,例如,
revocationTimeout.peer ! "woohoo" // compiles OK!
另一方面,如果我们查看相应的 Akka 类型化!
方法
implicit final class ActorRefOps[-T](val ref: ActorRef[T]) extends AnyVal {
def !(msg: T): Unit = ref.tell(msg)
}
然后我们看到它被类型参数T
参数化,编译器会捕获它。