【问题标题】:Working around overloading restriction (implementing a trait for several types)解决重载限制(实现多种类型的特征)
【发布时间】:2011-11-24 01:47:45
【问题描述】:

我知道不可能重载仅在返回类型上有所不同的方法。但我想知道是否有任何聪明的策略可以有效地处理这种情况:

trait Reader[A] { def read(in: java.io.DataInput): A }

trait B; trait C

def doSomethingB()(implicit r: Reader[B]) = ()
def doSomethingC()(implicit r: Reader[C]) = ()

trait MultiReader extends Reader[B] with Reader[C] { // not possible
  implicit me = this

  doSomethingB()
  doSomethingC()
}

我的意思是聪明和高效,我想避免像这样的混乱和不必要的内部类生成:

trait MultiReader {
  implicit object RB extends Reader[B] { ... }
  implicit object RC extends Reader[C] { ... }

  doSomethingB()
  doSomethingC()
}

编辑

这是一个部分解决方案。在过去的几天里,我一直在重读 Miles Sabin 的this gist,这似乎非常鼓舞人心。所以我可以做到以下几点:

type Tagged[U] = { type Tag = U }
type @@[T, U]  = T with Tagged[U]

trait Reader[A] { def read(in: java.io.DataInput @@ A): A }

然后就可以了:

trait MultiReader {
  def read(in: java.io.DataInput @@ B): B
  def read(in: java.io.DataInput @@ C): C
}

但是继承还是有些破损:

trait MultiReader extends Reader[B] with Reader[C]

(以"self-type MultiReader does not conform to Reader[B]'s selftype Reader[B]" 失败)。

【问题讨论】:

    标签: scala overloading


    【解决方案1】:

    这仍然为每个所需的类型参数实例化一个Function1 加上一个匿名Reader,但至少它在语法上更简洁:

    object Reader {
      implicit def fromFun[A](implicit fun: java.io.DataInput => A): Reader[A] =
        new Reader[A] { def read(in: java.io.DataInput): A = fun(in) }
    }
    trait Reader[A] { def read(in: java.io.DataInput): A }
    
    def doSomethingB()(implicit r: Reader[B]): Unit = println(r.read(null))
    def doSomethingC()(implicit r: Reader[C]): Unit = println(r.read(null))
    
    trait MultiReader {
      implicit def readB(in: java.io.DataInput): B = new B { override def toString = "B" }
      implicit def readC(in: java.io.DataInput): C = new C { override def toString = "C" }
    
       doSomethingB()
       doSomethingC()
    }
    
    new MultiReader {} // --> B, C
    

    【讨论】:

      【解决方案2】:

      主要问题似乎是 Scala 不允许多次实现泛型 trait,即使类型参数不同。有趣的是,虽然调用标记的版本可以正常工作(我可以故意使用标记为BC 的输入调用read),但在使用结构类型时它会失败,如下所示:

      def doSomethingB()(implicit r: { def read(in: java.io.DataInput @@ B): B }) = ()
      def doSomethingC()(implicit r: { def read(in: java.io.DataInput @@ C): C }) = ()
      

      这里有一个调度错误,两者都会调用读取C


      如果存在BC一个类层次结构中 的约束,一个想法是使用边界:

      sealed trait B; trait C extends B
      
      trait UpDownReader[Up, Down] {
        def read[A >: Down <: Up : Manifest](in: java.io.DataInput): A
      }
      
      class MultiReader(implicit mfx: Manifest[X], mfy: Manifest[Y])
        extends UpDownReader[X, Y] {
      
        def read[A >: Y <: X](in: java.io.DataInput)(implicit mf: Manifest[A]): A =
           (if (mf == mfx) new X {} else new Y {}).asInstanceOf[A]
      }
      

      这行得通:

      val m = new MultiReader
      m.read[B](null)
      m.read[C](null)
      

      但是,考虑到清单的“动态”比较以及对A 的丑陋转换,我认为这既不优雅也不高效。

      【讨论】:

        猜你喜欢
        • 2022-08-15
        • 2016-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-08
        • 2016-04-13
        相关资源
        最近更新 更多