Почему класс не может расширять черты с помощью метода с той же сигнатурой? - PullRequest
27 голосов
/ 03 декабря 2009

Почему я получаю ошибку ниже? Как это обойти?

Я предположил, что, поскольку A и B компилируются в пары (interface, class), вопрос выбора правильного статического вызова метода для реализации при компиляции C. Я ожидаю, что приоритет будет соответствовать порядку.

scala> trait A { def hi = println("A") }
defined trait A

scala> trait B { def hi = println("B") }
defined trait B

scala> class C extends B with A
<console>:6: error: error overriding method hi in trait B of type => Unit;
 method hi in trait A of type => Unit needs `override' modifier
       class C extends B with A

scala> trait A { override def hi = println("A") }
<console>:4: error: method hi overrides nothing
       trait A {override def hi = println("A")}

Обратите внимание, что в Ruby это работает хорошо:

>> module B; def hi; puts 'B'; end; end
=> nil
>> module A; def hi; puts 'A'; end; end
=> nil
>> class C; include A; include B; end
=> C
>> c = C.new
=> #<C:0xb7c51068>
>> c.hi
B
=> nil

Ответы [ 5 ]

52 голосов
/ 03 декабря 2009

Это работает для меня в 2.8 и 2.11 и позволит вам быть ненавязчивым в чертах A или B:

trait A { def hi = println("A") }
trait B { def hi = println("B") }

class C extends A with B {
  override def hi = super[B].hi
  def howdy = super[A].hi // if you still want A#hi available
}

object App extends Application {
  (new C).hi // prints "B"
}
12 голосов
/ 03 декабря 2009

Вы можете использовать общую базовую черту, скажем Base, следующим образом:

trait Base {def hi: Unit}
trait A extends Base {override def hi = println("A")}
trait B extends Base {override def hi = println("B")}
class C extends A with B

При использовании иерархии типов результат вызова hi выглядит следующим образом (обратите внимание на использование {} для создания признаков):

scala> (new A {}).hi
A

scala> (new B {}).hi
B

scala> (new C).hi
B
4 голосов
/ 03 декабря 2009

Черта добавляет методы к классу, который смешивает его. Если две черты добавляют один и тот же метод, класс получит два идентичных метода, чего, конечно, не может быть.

Однако, если метод является частным в признаке, это не вызовет проблем. И если вы хотите, чтобы методы накладывались друг на друга, вы можете определить базовую черту, а затем abstract override для наследующих черт. Однако для определения метода требуется класс. Вот пример этого:

scala> trait Hi { def hi: Unit }
defined trait Hi

scala> trait A extends Hi { abstract override def hi = { println("A"); super.hi } }
defined trait A

scala> trait B extends Hi { abstract override def hi = { println("B"); super.hi } }
defined trait B

scala> class NoHi extends Hi { def hi = () }
defined class NoHi

scala> class C extends NoHi with B with A
defined class C

scala> new C().hi
A
B

Однако, если вы действительно хотите два отдельных метода для каждой черты, тогда вам нужно написать вместо наследовать .

1 голос
/ 03 декабря 2009

Это проблема алмаза . Какой метод hi должен быть унаследован, один от A или один от B? Вы можете обойти это, как предложил Дон, используя общую базовую черту.

0 голосов
/ 08 сентября 2014

У меня была та же проблема, и мне не нравилось создавать промежуточную черту, потому что я могу иметь 4,5 или даже 6 черт с одинаковыми методами, потому что это черты, содержащие операции CRUD (найти, создать .. .). Кроме того, мне нужно было использовать эти черты вместе только для целей тестирования, и я всегда стараюсь максимально избегать изменения структуры моего проекта только для того, чтобы облегчить мой тест. Поэтому я просто реализовал эти черты в разных объектах:

class somethingToTest {
  object AImpl extends ATrait 
  object BImpl extends BTrait

  val a = AImpl.methodDuplicated()
  val b = BImpl.methodDuplicated()
}

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

...