【问题标题】:How to make the scala compiler find case classes used with wrong arguments如何让 scala 编译器找到使用错误参数的案例类
【发布时间】:2019-06-26 12:54:06
【问题描述】:

我有一个 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

[历史] 它曾经是case object Disconnect,但在某些时候它已更改为案例类并添加了一个参数,在代码中它仍然是无参数实例化的,编译器无法注意到它。我尝试将-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 的函数中作为参数。

【问题讨论】:

    标签: scala maven scalac


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

      它的签名是

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

      所以你可以向它发送任何东西,在这种情况下,你发送的是 Disconnect 类型。 这是使用 akka actor 的最大缺点之一,这就是为什么有一个新模块 akka typed 可以为您的 actor 定义类型安全的行为。

      您可能想知道,如果您正在发送一个您不期望的对象,为什么这不会在运行时发生故障。原因是actor的接收是一个PartialFunction[Any, Unit],它“丢弃”了没有为PF定义的消息。

      【讨论】:

        【解决方案3】:

        问题不在于编译器,而在于接受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 进行参数化,编译器会捕获它。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-02-17
          • 2021-08-11
          • 2016-07-20
          • 1970-01-01
          • 1970-01-01
          • 2021-09-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多