从类型参数获取子类型参数

时间:2018-10-09 21:11:55

标签: scala generics

让我们建立一些背景。 DataMetadataPayload组成。

trait Data[Metadata <: Product, Payload <: Product] extends Product {
  def metadata: Metadata
  def payload: Payload
}

case class M()
case class P()
case class D(metadata: M, payload: P) extends Data[M, P]

我有Processor处理MetadataPayload的方式有所不同。

trait MetadataProcessor[Metadata <: Product] {
  def apply() = "process metadata"
}
trait PayloadProcessor[Payload <: Product] {
  def apply() = "process payload"
}

// both work
new MetadataProcessor[M]{}.apply() // "process metadata"
new PayloadProcessor[P]{}.apply() // "process payload"

我希望DataProcessor能够同时处理MetadataPayload。这是一种方法:

trait DataProcessor[Metadata <: Product, Payload <: Product] {
  def apply() = {
        new MetadataProcessor[Metadata]{}.apply() +
        new PayloadProcessor[Payload]{}.apply()   
  }
}
new DataProcessor[M,P]{}.apply() // "process metadataprocess payload"

但是,我想调用new DataProcessor[D]{}.apply()并获得完全相同的输出。怎么做?

对于具有以上代码的交互式Scala环境,请随时关注Scastie playground

1 个答案:

答案 0 :(得分:1)

尝试制作MetadataPayload type members rather than type parameters,并使用类型投影Data#MetadataData#Payload

trait Data extends Product {
  type Metadata <: Product
  type Payload <: Product
  def metadata: Metadata
  def payload: Payload
}

  // This is optional, just in case if you need also type parameters Metadata and Payload.
  // Then you can use them as Data.Aux[Metadata, Payload]
//  object Data {
//    type Aux[Metadata0 <: Product, Payload0 <: Product] = Data { type Metadata = Metadata0; type Payload = Payload0 }
//  }

case class M()
case class P()
case class D(metadata: M, payload: P) extends Data { type Metadata = M; type Payload = P }

trait MetadataProcessor[Metadata <: Product] {
  def apply() = "process metadata"
}
trait PayloadProcessor[Payload <: Product] {
  def apply() = "process payload"
}

new MetadataProcessor[M]{}.apply() // "process metadata"
new PayloadProcessor[P]{}.apply() // "process payload"

trait DataProcessor[A <: Data] {
  def apply() = {
    new MetadataProcessor[A#Metadata]{}.apply() +
      new PayloadProcessor[A#Payload]{}.apply()
  }
}

new DataProcessor[D]{}.apply() // "process metadataprocess payload"