如何让 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
选项 运行 编译器,但它仍然无法捕获错误。
- scala 版本:2.11.12
- 目标jvm版本:1.8
- 使用选项编译的代码:
-deprecation
-feature
-language:postfixOps
-language:implicitConversions
-Xfatal-warnings
-unchecked
-Xmax-classfile-name 140
-nobootcp
[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
,其中包含所有整齐的 apply
和 unapply
方法。因此,Peer.Disconnect
是单例类型 Peer.Disconnect.type
的完全有效表达式。有人可能会争辩说,如果您从一开始就使用 Akka Typed 就不会发生这种情况,但是在您的代码中, !
方法接受任何东西,因此为了强制编译器发出一些有意义的错误消息,您需要别的东西。这是一种简单的方法:
- 恢复到
Disconnect
是单例对象的状态,没有关联的大小写 class。
- 完全删除
Disconnect
定义。添加 case class NewDisconnect
代替。现在每次出现 Peer.Disconnect
都会变成 正确的错误 .
- 将所有
Peer.Disconnect
替换为 Peer.NewDisconnect(foo)
- 将
NewDisconnect
重命名为 Disconnect
。
我有一个 case class Disconnect(nodeId: PublicKey)
,里面有 1 个参数,但是在代码的其他部分,它恰好在没有参数的情况下使用,即:Disconnect
,编译器没有捕捉到错误,请注意,我还尝试使用 -Xlint
选项 运行 编译器,但它仍然无法捕获错误。
- scala 版本:2.11.12
- 目标jvm版本:1.8
- 使用选项编译的代码:
-deprecation
-feature
-language:postfixOps
-language:implicitConversions
-Xfatal-warnings
-unchecked
-Xmax-classfile-name 140
-nobootcp
[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
,其中包含所有整齐的 apply
和 unapply
方法。因此,Peer.Disconnect
是单例类型 Peer.Disconnect.type
的完全有效表达式。有人可能会争辩说,如果您从一开始就使用 Akka Typed 就不会发生这种情况,但是在您的代码中, !
方法接受任何东西,因此为了强制编译器发出一些有意义的错误消息,您需要别的东西。这是一种简单的方法:
- 恢复到
Disconnect
是单例对象的状态,没有关联的大小写 class。 - 完全删除
Disconnect
定义。添加case class NewDisconnect
代替。现在每次出现Peer.Disconnect
都会变成 正确的错误 . - 将所有
Peer.Disconnect
替换为Peer.NewDisconnect(foo)
- 将
NewDisconnect
重命名为Disconnect
。