Я смущен тем, как работают ограничения в генераторах Scala. Рассмотрим следующий пример:Scala generics: почему два типа с одинаковой верхней границей несовместимы?
sealed trait MessageBody
case class Body1() extends MessageBody
case class Body2() extends MessageBody
sealed trait MessageKey[T <: MessageBody]
case object Key1 extends MessageKey[Body1]
case object Key2 extends MessageKey[Body2]
type MessageHandler[B <: MessageBody] = PartialFunction[MessageKey[B], B => Unit]
abstract class Handler {
def messageHandler[B <: MessageBody]: MessageHandler[B]
}
class ProcessingHandler[ReturnType <: MessageBody](val onSuccess: MessageKey[ReturnType]) extends Handler {
override def messageHandler[B <: MessageBody]: MessageHandler[B] = {
case `onSuccess` => success
}
def success(msg: ReturnType): Unit = println(msg)
}
Это дает мне ошибку:
error: type mismatch;
found : ReturnType => Unit
required: B => Unit
case `onSuccess` => success
^
В моем наивном понимании, как ReturnType
и B
являются подтипы MessageBody
и, следовательно, должны быть совместимы. Почему они не совместимы и как мне написать свой код, чтобы сделать их совместимыми?
EDIT: Следующий фрагмент кода работает нормально, и если я смешиваю связь между MessageKey
и MessageBody
экземпляров в любом месте, я получаю хорошую ошибку во время компиляции.
class ProcessingHandler2 extends Handler {
override def messageHandler[B <: MessageBody]: MessageHandler[B] = {
case Key1 => h1
// case Key1 => h2 // gives compile-time error
case Key2 => h2
}
def h1(x: Body1): Unit = println(x)
def h2(x: Body2): Unit = println(x)
}
val handler: Handler = new ProcessingHandler2
handler.messageHandler(Key1)(Body1())
// handler.messageHandler(Key1)(Body2()) // gives compile-time error
EDIT: Очевидно, что нет никакого способа компиляции проверить это: handler.messageHandler(Key3)
, и это приведет к MatchError
во время выполнения.
В случае, если я вызываю 'messageHandler [Body2]' на объект типа 'ProcessingHandler [Body1]' Я ожидаю ошибку времени компиляции. По той же причине я не могу заставить 'success' взять' MessageBody', потому что тогда я теряю безопасность статического типа. И я не могу создать «Handler» generic, потому что он также имеет другие подклассы, которые имеют много методов обработчика сообщений с различными сигнатурами. – Mahdi
@Mahdi Это не то, как работают дженерики, и это не будет действительной реализацией интерфейса. В вашем интерфейсе говорится, что «метод« messageHandler »принимает параметр типа, который должен быть подклассом« MessageBody ». Поэтому, если у вас есть реализация этого интерфейса, и вы вызываете его с параметром типа, который является подклассом «MessageBody», нет никакой возможной причины, почему он не должен компилироваться. Проблема в том, что ваша реализация интерфейса делает то, что работает только с определенными параметрами типа. Это ошибка в реализации, а не использование метода. – sepp2k
В отредактированном вопросе показан тип ошибок времени компиляции, которые я ожидаю, и которые я получаю. Я был бы признателен, если бы вы также могли помочь мне с примером того, как вы ожидаете, что что-то пойдет не так, пока компилятор не сможет их найти. – Mahdi