2010-07-04 5 views
6

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

class A { 
    def b { } 
} 

Я продлить этот класс в Scala и переопределить метод, чтобы сделать его аннотация:

abstract class AA extends A { 
    override def b 
} 

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

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

Если я расширяю АА с чертом 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 дня). Еще одна странного и связанное с поведением является то, что метка переопределения не является необходимой при проведении б аннотации:

abstract class AA extends A { 
    def b 
} 
+0

Является ли такая же проблема, как если бы вы хотели заменить реализацию Java toString класса Java, смешав свою собственную реализацию 'def toString(): String', которую вы определили бы в черте? – huynhjl

+0

Нет, это не тот случай. В этом случае поведение компилятора делает общий смысл, потому что неоднозначно, какое определение должно использоваться: Java toString или mixin one. В моем случае есть только один доступный. –

ответ

2

Не уверены, что это правильное решение, но если ваш trait 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 = [email protected] 

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 (для того, чтобы иметь возможность добавить 'override'):

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 = [email protected] 

scala> res7.b 
B b 

Права b переопределенного метода называется с C.b


Что касается вашего явного «несоответствие», см Scala for Java Refugees Part 5: Traits and Types:

To start with, there’s that ever-annoying override keyword. I mentioned back in the article on basic OOP that any method which overrides a method in a superclass must be declared with the override modifier. At the time, I likened it to the language mandating the use of the @Override annotation, with its primary purpose being to enforce the good practice.

The real key to the power of traits is the way in which the compiler treats them in an inheriting class.
Traits are actually mixins, not true parent classes.
Any non-abstract trait members are actually included in the inheriting class, as in physically part of the class. Well, not physically, but you get the picture.
It’s as if the compiler performs a cut-and-paste with the non-abstract members and inserts them into the inheriting class. This means that there’s no ambiguity in the inheritance path, meaning no diamond problem.

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

+0

Спасибо за подробный ответ. Я думаю, что я понимаю черты, поскольку я родом из Ruby, у которого есть модули, что-то вроде того. IMHO, создающий черту B extend A, побеждает цель черт, так как я не могу использовать эту черту в другой иерархии классов, и именно этого я и пытаюсь сделать. Есть идеи? Я думаю, что проблема заключается в компиляторе Scala, который не видит, что метод b стал абстрактным, и нет никакой двусмысленности в его определении при смешивании в черте в классе листа C. –

+0

@ A.R: Я понимаю; мой ответ был вдохновлен http://blog.objectmentor.com/articles/2008/09/29/a-scala-style-_with_-construct-for-ruby. Все еще проверяю другие решения. – VonC

5

Чтобы попытаться понять, что происходит, я попытался это:

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'.

+0

Ну, переопределение можно легко устранить, отменив двусмысленность для компилятора с переопределением def b = super [B] .b в классе C, но это не обязательно. Это моя точка зрения. Благодаря! –

+0

MJP прав: конкретные участники никогда не могут быть сделаны абстрактными в подклассе. Объявление абстрактного члена в подклассе не влияет. Это является следствием правил составления смеси, где конкретный всегда имеет преимущество перед абстрактным, независимо от того, какой порядок признаков содержит определения. –

2

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

Вы сделали:

class A { 
    def b { } 
} 

abstract class AA extends A { 
    override def b 
} 

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

Поэтому, когда вы смешиваете AA и B метод б определяется дважды. После того, как с помощью (Не перекрытая, так как определение в B заменен переопределение в AA), а второй B, компилятор не может выбрать один над другим, потому что нет никакой иерархии между ними (equaly, но не связанные). Если хотите, подумайте об этом так: компилятор «смешивает» тела AA и B; если он отклонит метод от AA, он будет абстрактным, если он выберет метод от B (Что должно произойти), поскольку это не переопределение, вы застряли в двух методах: b.

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

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

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. Сделать б в абстрактном (Но вы не хотите, что)

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

+0

Спасибо, ваше объяснение было очень ясным. –

Смежные вопросы