Подтвердить что ты не робот

Почему класс не может расширять черты с помощью метода одной и той же подписи?

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

Я предположил, что, поскольку 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
4b9b3361

Ответ 1

Это работает для меня в 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"
}

Ответ 2

Вы можете использовать общий базовый признак, например 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

Ответ 3

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

Если этот метод является частным в этом признаке, это не вызовет проблемы. И если вы хотите, чтобы методы стекались друг над другом, вы можете определить базовый признак, а затем 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

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

Ответ 4

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

Ответ 5

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

class somethingToTest {
  object AImpl extends ATrait 
  object BImpl extends BTrait

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

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