Как заставить компилятор scala находить классы случаев, используемые с неверными аргументами - PullRequest
2 голосов
/ 26 июня 2019

У меня есть 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, но в какой-то момент он был изменен на 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 используется как тип вместо экземпляра класса case, это возможно, потому что он используется в функции, которая принимает Any как параметр.

Ответы [ 3 ]

2 голосов
/ 26 июня 2019

Поскольку вы объявляете Disconnect как case class, компилятор автоматически генерирует компаньон object Disconnect, который содержит все аккуратные методы apply и unapply.Следовательно, Peer.Disconnect является совершенно корректным выражением типа singleton Peer.Disconnect.type.Кто-то может возразить, что этого бы не произошло, если бы вы использовали Akka Typed с самого начала, но в вашем коде метод ! принимает все, поэтому для того, чтобы заставить компилятор выдавать какие-то значимые сообщения об ошибках, вам нужно что-тоостальное.Вот один простой подход:

  1. Возврат к состоянию, когда Disconnect был одноэлементным объектом, без связанного с ним класса дела.
  2. Удалите определение Disconnect в целом.Вместо этого добавьте case class NewDisconnect.Теперь каждое вхождение Peer.Disconnect станет правильной ошибкой .
  3. Замените все Peer.Disconnect на Peer.NewDisconnect(foo)
  4. Переименуйте NewDisconnect в Disconnect.
1 голос
/ 26 июня 2019

Я предполагаю, что ! является оператором Tell от актеров akka.

Это подпись

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

Таким образом, вы можете отправить ему буквально все, что угодно, в этом случае вы отправляете тип Disconnect. Это один из самых больших недостатков использования актеров akka, поэтому существует новый модуль akka с типом , где вы определяете безопасное для типов поведение для ваших актеров.

Вы можете задаться вопросом, почему это не взрывается во время выполнения, если вы отправляете объект, который вы не ожидаете. Причина в том, что актер получает функцию PartialFunction [Any, Unit], которая «отбрасывает» сообщения, которые не определены для PF.

1 голос
/ 26 июня 2019

Проблема не в компиляторе, а в методе !, который принимает 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 и компилятор его поймает.

...