Как заставить компилятор scala найти классы case, используемые с неправильными аргументами

У меня есть case class Disconnect(nodeId: PublicKey) с 1 параметром, однако в какой-то другой части кода он использовался без параметра, т.е.: 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 class и был добавлен параметр, в коде он по-прежнему создавался без параметров, и компилятор не мог этого заметить. Я пробовал добавить в компилятор опцию -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 в качестве параметра.


person Andrea Raspitzu    schedule 26.06.2019    source источник


Ответы (3)


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

  1. Возврат к состоянию, в котором Disconnect был одноэлементным объектом без связанного с ним класса case.
  2. Полностью удалите определение Disconnect. Вместо этого добавьте case class NewDisconnect. Теперь каждое появление Peer.Disconnect будет считаться правильной ошибкой.
  3. Замените все Peer.Disconnect на Peer.NewDisconnect(foo)
  4. Переименуйте NewDisconnect в Disconnect.
person Andrey Tyukin    schedule 26.06.2019

Я предполагаю, что ! - это оператор Tell от актеров akka.

Его подпись

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

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

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

person gabrielgiussi    schedule 26.06.2019

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

person Mario Galic    schedule 26.06.2019