Как переопределить универсальный метод с неявными аргументами, если универсальный тип уже зафиксирован? - PullRequest
2 голосов
/ 03 сентября 2011

Я пытаюсь переопределить этот метод

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?

Я не уверен, какие типы и следствия я могу опустить, а что нужно оставить, чтобы метод по-прежнему переопределял.

Ответы [ 2 ]

3 голосов
/ 03 сентября 2011

Первая проблема заключается в том, что переопределенным методам требуется одинаковое количество и тип параметров типа, даже если они не используются. Например,

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. Это означает, что нет никаких подтипных отношений между Numeric[B] и Numeric[C], если B и 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 голосов
/ 04 сентября 2011

Хорошо, пытаясь объяснить, почему правила должны заставлять вас сохранять подпись с неявным параметром и дисперсией.

Во-первых, неявный аргумент по-прежнему является аргументом, он может передаваться явно, и, за исключением случаев, когда он имеет одноэлементный тип (что не очень полезно), возможны несколько различных его экземпляров.

Предположим, я создаю

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

Похоже на правильное Numeric. В документации Numeric не указано, какие законы следует ожидать, но 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). Так что ваш подкласс тоже должен это разрешать.

Поэтому позволить вам удалить неявный параметр или дисперсию в подклассе было бы неправильно.

...