Реализация абстрактного метода с чертой, непоследовательным поведением компилятора?

У меня есть базовый класс, который прибывает из библиотеки Java, код которой я не могу изменить. Этот класс (A) имеет пустой метод (b), который должен был быть объявлен как краткий обзор вместо этого:

class A {
  def b { }
}

Я расширяю этот класс в Scala и переопределяю метод для создания его кратким обзором:

abstract class AA extends A {
  override def b
}

Теперь я реализую этот метод в черте:

trait B {
  def b { println("B") }
}

Если я расширяю AA с помощью черты B, я получаю ошибку: метод переопределения b в классе A типа => Единица; для метода b в черте B типа => Единица нужен модификатор 'переопределения':

class C extends AA with B {}

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

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

Я выполняю Scala 2.8.0RC3, и абсолютно плохо знакомый с языком (3 дня). Другое странное и связанное поведение состоит в том, что маркировка переопределения не необходима при создании b краткого обзора:

abstract class AA extends A {
  def b
}
6
задан A.R 4 July 2010 в 10:23
поделиться

3 ответа

Чтобы попытаться понять, что происходит, я попробовал следующее:

scala> class A{
     |   def b{ }
     | }
defined class A

scala> abstract class AA extends A{
     |   override def b
     | }
defined class AA

scala> class AAA extends AA{
     |   def b = println("AAA")
     | }
<console>:8: error: overriding method b in class A of type => Unit;
 method b needs `override' modifier
         def b = println("AAA")
             ^

Очевидно, источник проблемы в том, что абстрактные классы не могут "освободить" методы в своем суперклассе от необходимости включения модификатора 'override' в подклассы абстрактного класса.

5
ответ дан 10 December 2019 в 02:42
поделиться

Не уверен, что это правильное решение, но если ваша черта B расширяет A (и отменяет b ), тогда все компилируется нормально:

Сначала давайте определим A и AA , как вы представляете их в своем вопросе:

C:\Users\VonC>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class A {
     | def b { println("A b") }
     | }
defined class A

scala> new A
res5: A = A@153bedc4

scala> res5.b
A b

scala> abstract class AA extends A {
     | override def b
     | }
defined class AA

Что вы сделали:

scala> trait B {
     | override def b { println("B b") }
     | }
<console>:6: error: method b overrides nothing
       override def b { println("B b") }
                    ^

Что я пробовал с чертой B (чтобы иметь возможность добавить « переопределение »):

scala> trait B extends A {
     | override def b { println("B b") }
     | }
defined trait B

Итак, теперь:

scala> class C extends AA with B {}
defined class C

scala> new C
res7: C = C@1497b7b1

scala> res7.b
B b

Правый переопределенный метод b вызывается с помощью Cb


Что касается вашей очевидной «несогласованности», см. Scala для Java Refugees, часть 5: Характеристики и типы :

Для начала есть постоянно раздражающее ключевое слово override .Еще в статье о базовом ООП я упоминал, что любой метод, который переопределяет метод в суперклассе, должен быть объявлен с модификатором override. В то время я сравнил его с языком, требующим использования аннотации @Override , основной целью которой было закрепление передовой практики.

Настоящий ключ к силе свойств - это то, как компилятор обрабатывает их в наследуемом классе.
Признаки на самом деле являются миксинами, а не настоящими родительскими классами .
Любые неабстрактные члены признака фактически включаются в наследующий класс, как в физическую часть класса. Ну, не физически, но вы понимаете.
Это как если бы компилятор выполнял вырезание и вставку с неабстрактными членами и вставлял их в наследующий класс. Это означает, что в пути наследования нет двусмысленности, что означает отсутствие проблемы с ромбами.

Таким образом, во втором примере нет необходимости в ключевом слове override .

2
ответ дан 10 December 2019 в 02:42
поделиться

Проблема очень тонкая. Как показывает опыт, ваш класс AA , который расширяет A , должен быть смешан с чертами, которые также расширяют A .

Вы сделали:

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B {
  def b { println("B") }
}

Следовательно, когда вы смешиваете AA и B метод b ОПРЕДЕЛЕН дважды. Один раз на A (не отменяется, потому что определение в B заменяет отмену в AA ), а второй - на B , компилятор может Я не выбираю один из них, потому что между двумя (одинаково названными, но не связанными) методами нет иерархии. Если хотите, подумайте об этом так: компилятор «смешивает» тела AA и B ; если он выберет метод из AA , он будет абстрактным, если он выберет метод из B (что должно произойти), так как это не переопределение, вы застряли с двумя методами b .

Чтобы решить эту проблему, вы должны убедиться, что оба метода переопределяют один и тот же метод, и в этом случае компилятор поймет, что вы говорите об ОДНОМ методе, и отдаст приоритет последнему смешанному признаку. .

Теперь, чтобы переопределить метод b в B , этот класс также должен унаследовать от A . Итак, канонический способ сделать это:

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B extends A{
  def b { println("B") }
}

class C extends AA with B {}

Какой компилируется нормально.

Теперь, когда вы это сделаете:

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

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

Другие решения включают:

  1. Сделать B переопределить AA
  2. Сделать b в A аннотация (Но вы не хотели that)

Опять же, проблема очень тонкая, но я надеюсь, что я прояснил ее. Чтобы лучше понять, прочтите Stackable Trait Pattern Scala .

2
ответ дан 10 December 2019 в 02:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: