Scala泛型:为什么具有相同上限的两个类型不兼容?

时间:2015-12-15 12:52:27

标签: scala generics type-bounds

我对Scala泛型中的界限如何工作感到困惑。请考虑以下事项:

  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
                                 ^

在我天真的理解中,ReturnTypeB都是MessageBody的子类型,因此必须兼容。那么为什么它们不兼容,我应该如何编写代码以使它们兼容呢?

编辑:下面的一段代码工作正常,如果我在任何地方混合MessageKeyMessageBody实例之间的关系,我会得到一个很好的编译时错误。

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

编辑:显然,没有办法编译检查:handler.messageHandler(Key3)这将在运行时产生MatchError

1 个答案:

答案 0 :(得分:2)

假设您有ProcessingHandler[Body1]类型的对象,然后在其上调用messageHandler[Body2]。根据签名,它应返回Body2 => Unit,但您尝试返回Body1 => Unit。如果您尝试返回MessageBody => Unit,那就没问题,因为MessageBodyBody2的超类,但Body1不是。Body2。它们都是同一个超类的子类,但这并不能使它们兼容,也就是说你不能将Body1作为参数传递给一个带Handler的函数,反之亦然。

根据您的要求,您可以做多件事:

您可以messageHandler通用而不是Handler[ReturnType],然后扩展Handler(或者您可以为messageHandler提供成员类型并在success中使用它

或者您可以MessageBodyReturnType作为参数,而不是-Dosgi.requiredJavaVersion=1.7