У меня есть базовый класс, который прибывает из библиотеки 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
}
Чтобы попытаться понять, что происходит, я попробовал следующее:
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' в подклассы абстрактного класса.
Не уверен, что это правильное решение, но если ваша черта 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
.
Проблема очень тонкая. Как показывает опыт, ваш класс 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 {}
ясно, что оба метода одинаковы, поэтому компилятор знает, что он должен использовать метод из трейта.
Другие решения включают:
Опять же, проблема очень тонкая, но я надеюсь, что я прояснил ее. Чтобы лучше понять, прочтите Stackable Trait Pattern Scala .