2009-12-02 7 views
27

Warum ist der Fehler unten? Wie umgehen Sie es?Warum kann eine Klasse keine Merkmale mit derselben Signatur erweitern?

EDIT: Ich nahm an, dass seit A und B zu (Schnittstelle, Klasse) Paare kompilieren, es ist eine Frage der Wahl der richtigen statischen Methodenaufruf zu implementieren, wenn C. C. Ich würde erwarten, dass die Priorität nach Reihenfolge sein.

 
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 
: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")} 
:4: error: method hi overrides nothing 
     trait A {override def hi = println("A")} 

EDIT: beachten Sie, dass in Ruby das funktioniert gut:

 
>> 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.hi 
B 
=> nil 

Antwort

51

Dies funktioniert für mich in 2.8 und 2.11 und Ihnen erlauben würde, nicht-intrusive in Züge A oder B zu sein:

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" 
} 
+3

Excellent! Schade, dass, wenn ich versuche, Klasse C erweitert A mit B ', der Fehler diese Art der Konfliktlösung nicht erwähnt. – IttayD

+0

@IttayD mit Scala 2.10.4: Die Fehlermeldung besagt, wie dieses Problem gelöst werden kann: (Hinweis: Dies kann durch Deklarieren einer Überschreibung in Klasse C gelöst werden.) Klasse C erweitert A mit B { –

12

Sie eine gemeinsame Basis Merkmal verwenden könnte, sagen Base wie folgt:

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 

Mit der Typenhierarchie der Ergebnis des Aufrufs hi ist wie folgt (beachten Sie die Verwendung von {}, um die Merkmale zu instanziieren):

scala> (new A {}).hi 
A 

scala> (new B {}).hi 
B 

scala> (new C).hi 
B 
+0

Danke. Ich habe auf eine nicht aufdringliche Lösung gehofft. Etwas, das nur in C erledigt werden kann (als wären A und B von verschiedenen Drittanbieter-Bibliotheken) – IttayD

1

Dies ist die diamond problem. Welche Methode sollte hi vererbt werden, die aus A oder die aus B? Sie können dies umgehen, wie Don vorgeschlagen hat, indem Sie eine gemeinsame Grundeigenschaft verwenden.

+0

Siehe mein Ruby-Beispiel, wo alles in Ordnung ist. Es geht darum, die Semantik von "Super" zu entscheiden. Ich kann vorschlagen, dass im Baum der erweiterten Merkmale die erste DFS-weise ausgewählt wird. Beachten Sie auch, dass hier kein Diamant ist. Nur A, B, C. Im Gegenteil, die Lösung besteht darin, ein Basismerkmal zu erzeugen und so einen Diamanten zu erzeugen.Also ein Diamant ist eine Lösung, kein Problem hier – IttayD

+4

Es gibt kein Diamant Problem in Scala, wegen [Klassenlinearisierung] (http://jim-mcbeath.blogspot.com/2009/08/scala-class-linearization.html) ... – paradigmatic

4

Ein Merkmal fügt der Klasse, die es mischt, Methoden hinzu. Wenn zwei Merkmale die gleiche Methode hinzufügen, würde die Klasse mit zwei identischen Methoden enden, was natürlich nicht passieren kann.

Wenn die Methode in dem Merkmal privat ist, wird es jedoch kein Problem verursachen. Und wenn Sie möchten, dass die Methoden übereinander gestapelt werden, können Sie ein Basismerkmal und dann abstract override für die ererbenden Merkmale definieren. Es erfordert jedoch eine Klasse, um die Methode zu definieren. Hier ist ein Beispiel dafür:

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 

Wenn Sie jedoch wirklich zwei getrennte Methoden wollen aus jedem Zug, dann werden Sie zu brauchen zu komponieren statt erben.

+0

Warum sollte der Compiler nicht einfach eine Methode als implementierende wählen? Da A und B mit statischen Methoden zur Schnittstelle A und Klasse A $ -Klasse kompiliert werden, ist es in der JVM für C völlig in Ordnung, sowohl A als auch B zu implementieren, und die Implementierung der Methode hi in C kann einfach die A $ -Klasse aufrufen .hi – IttayD

+0

Beachten Sie, dass C in Ihrer Antwort mit zwei (sogar drei) identischen Methoden endet, so dass diese Sache erledigt werden kann. – IttayD

+1

Wenn man bereit wäre, die konzeptionelle Basis für die Vererbung wegzuwerfen, ja, könnte es getan werden - und es könnte später bereut werden. Wenn Sie Komposition, Komposition, nicht erben wollen. –

0

Ich hatte das gleiche Problem und Ich mag zu nicht haben zu erstellen ein intermediäres Merkmal, weil ich 4,5 oder sogar 6 Merkmale mit den gleichen Methoden haben kann, weil es Merkmale sind, die CRUD-Operationen enthalten (find, create ...). Außerdem musste ich diese Eigenschaften nur für Testzwecke verwenden und versuche immer so viel wie möglich zu vermeiden, die Struktur meines Projekts zu verändern, nur um meinen Test zu vereinfachen. So einfach ich diese Eigenschaften in verschiedenen Objekte implementiert:

class somethingToTest { 
    object AImpl extends ATrait 
    object BImpl extends BTrait 

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

Es ist wahrscheinlich nicht die clevere Art, Züge zu benutzen, aber es erfordert keine Änderung im Code des Projekts, es bedeutet nur ein bisschen mehr haben Code in den Tests.

Verwandte Themen