【问题标题】:How to override a generic method with implicit arguments if the generic type is already fixed?如果泛型类型已经固定,如何用隐式参数覆盖泛型方法?
【发布时间】:2011-09-03 18:22:23
【问题描述】:

我尝试覆盖这个方法

def sum[B >: A](implicit num: Numeric[B]): B = ...

A 类型已固定为 Int 的子类中。

我已经试过了

override def sum: Int = ...

但这当然不会覆盖,导致运行时基于动态类型的不同方法解析。

走得更远,

def sum[B >: Int](implicit num: Numeric[B]): Int

确实覆盖,而

def sum[B >: Int](implicit num: Numeric[Int]): Int

没有,以及

def sum(implicit num: Numeric[Int]): Int

为什么会这样?是否有可能摆脱多余的绑定B

我不确定哪些类型和隐式可以省略,哪些必须保留以使方法仍然覆盖。

【问题讨论】:

    标签: generics scala methods overriding implicit


    【解决方案1】:

    第一个问题是被覆盖的方法需要相同数量和种类的类型参数,即使它们没有被使用。例如,

    class C1 {
      def f[B] = println("hello")
    }
    
    class C2 extends C1 {
      override def f = println("world") // Error: f overrides nothing (needs parameter B)
    }
    

    除此之外,还有一个健全性问题,因为Numeric[A]A 中是不变的。这意味着只要 BC 不同,Numeric[B]Numeric[C] 之间就没有子类型关系。

    想象Numeric 是具有适当方差的特征,它仍然不起作用;显然,重写方法的签名需要完全相同:

    class D1 {
      def g(x: Int) {}
    }
    
    class D2 extends D1 {
      override def g(x: Any) {} // Error: g overrides nothing (different parameter type)
    }
    

    我不确定为什么不能扩大覆盖方法的类型。 编辑:可能是因为兼容重载,如

    class D1 {
      def g(x: Any) {}
      def g(x: Int) {} // This is legal, even though the definitions seem to overlap
    }
    

    总而言之,当你重写时,你必须保留方法的签名,包括类型参数。在你的情况下,这是你能做的最好的:

    override def sum[B >: Int](implicit num: Numeric[B]): B = ...
    

    【讨论】:

    • 那么我的方法应该是什么样子呢?
    • 你要编译的版本是你能做的最好的。
    【解决方案2】:

    好的,试图解释为什么规则必须强制您保留带有隐式参数和方差的签名。

    首先,隐式参数仍然是一个参数,它可以显式传递,并且除了可能是单例类型(这不是很有用)之外,它的几个不同实例都是可能的。

    假设我创建了

    case class ZModulo(val p: Int) extends Numeric[Int] {
      def plus(a: Int, b: Int) = (a+b) % p
      // others along the same line
    }
    

    这似乎是一个正确的NumericNumeric 文档没有说明应该遵循哪些法律,但 ZModulo 并非不合理。

    现在有你的

    class Summable[A] {
      def sum[B >: A](implicit num: Numeric[A]): B =...
    }
    

    如果我有val ints : Summable[Int],我当然可以拨打ints.Sum(ZModulo(3))。因此,如果您的课程是Summable[Int] 的子类,它必须允许我这样做。所以你不能删除Numeric 参数。

    其次,假设我带有Numeric[Any]。不确定如何合理地为数字做到这一点,但规范和编译器不知道这一点。无论如何,他们也必须接受不合理的实现。所以让我们来

    object MixThemAll : Numeric[A] {...}
    

    Summable 中的签名允许ints.sum(MixThemAll)。所以你的子类也必须允许。

    因此,让您删除子类中的隐式参数或方差是不合理的。

    【讨论】:

    • 确实如此。我发现有趣的是,即使事情是正确的(我的回答中是 C2 extends C1D2 extends D1 类),更改签名仍然是非法的。
    • 在 D 上,我认为你是对的,原因是它会使与重载相关的规则变得一团糟。在C上,我不知道。您甚至可以考虑将通用结果替换为非通用下限。但是,编写泛型参数并没有不良后果。所以也许它不值得一个特殊的规则来允许它?
    猜你喜欢
    • 2016-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    • 2012-03-01
    • 1970-01-01
    • 2020-03-14
    相关资源
    最近更新 更多