2013-01-05 16 views
6

Mam abstrakcyjną klasę podstawową (Base), która ma zdefiniowane pewne cechy układania (StackingTrait).Scala: Ciekawostka Mixin z abstrakcyjną podstawową klasą

trait Base { 
    def foo 
} 
trait StackingTrait extends Base { 
    abstract override def foo { super.foo } 
} 

Byłoby to bardzo wygodne, aby zaimplementować podklasa używając następującej składni, ale to nie działa, ponieważ kompilator mówi, że foo musi być zadeklarowana z override a następnie abstract override o ponownej kompilacji, które jest nieważne, ponieważ Impl to klasa.

class Impl extends Base with StackingTrait { 
    def foo {} 
} 

Nie mogę myśleć o dobry powód, dlaczego tak składnia będzie niedozwolonych; foo jest logicznie zdefiniowane za pomocą Impl, więc zamawianie układania w konceptualnie pozostaje takie samo.

Uwaga: Uwaga: Wymyśliłem to obejście, które skutecznie zrobi to samo, co chcę, ale konieczność klasy pomocniczej sprawia, że ​​chcę lepszego rozwiązania.

class ImplHelper extends Base { 
    def foo {} 
} 
class Impl extends ImplHelper with StackingTrait 

Dlaczego żądana składnia nie jest skompilowana i czy istnieje eleganckie rozwiązanie?

Odpowiedz

4

Rozumiem, że chociaż komunikat o błędzie może być mylący, zachowanie jest prawidłowe. foo została zadeklarowana jako abstract override w StackingTrait, w ten sposób jakiejkolwiek konkretnej klasy, która łączy StackingTrait musi istnieć konkretny (nie oznaczone jako abstract) realizacja fooprzedStackingTrait (w stosunku do celu linearyzacji). Dzieje się tak, ponieważ super odnosi się do cechy tuż przed w kolejności linearyzacji, więc zdecydowanie musi być konkretna implementacja foo, zanim StackingTrait zostanie wymieszana, lub super.foo będzie bezsensowna.

Kiedy to zrobić:

class Impl extends Base with StackingTrait { 
    def foo {} 
} 

kolejność linearyzacja jest Base < - StackingTrait < - Impl. Jedyną cechą przed StackingTrait jest Base i Base nie definiuje konkretnej implementacji foo.

Ale kiedy to zrobić:

traitImplHelper extends Base { 
    def foo {} 
} 
class Impl extends ImplHelper with StackingTrait 

Kolejność linearyzacja staje: Base < - ImplHelper < - StackingTrait < - Impl Tutaj ImplHelper zawiera konkretną definicję foo, i jest zdecydowanie przedStackingTrait.

Co jest warte, gdybyś zmieszał ImplHelper po StackingTrait (jak w class Impl extends StackingTrait with ImplHelper), znów miałbyś ten sam problem i nie udałoby się go skompilować.

Wygląda to dość spójnie. Nie jestem świadomy sposobu, aby go skompilować zgodnie z zamierzeniem.Jeśli jednak bardziej zależy Ci na tym, aby łatwiej napisać Impl (i móc zdefiniować foo właśnie tam, bez potrzeby oddzielnej klasy/cechy), niż ułatwić pisanie Base lub StackingTrait, nadal możesz to zrobić:

trait Base { 
    protected def fooImpl 
    def foo { fooImpl } 
} 
trait StackingTrait extends Base { 
    abstract override def foo { super.foo } 
} 

class Impl extends Base with StackingTrait { 
    protected def fooImpl {} 
} 

Tak jak w wersji oryginalnej, wymuszasz każdą klasę betonu na implementację foo (w postaci fooImpl) i tym razem kompiluje się. Wadą jest to, że podczas gdy fooImpl nie może wywoływać super.foo (to nie ma sensu i wejdzie w nieskończoną pętlę), kompilator nie ostrzeże Cię o tym.

+0

Świetna odpowiedź! Właśnie miałem zadać pytanie na temat tego właśnie scenariusza. Dzięki! –

+0

Właśnie napotkałem ten problem. Linearyzacja jest w tym przypadku słuszna, ale zdałem sobie sprawę z tego, że rzeczywisty cel układania cech jest następujący: Możesz wprowadzić modyfikację z cechą stosu tylko do istniejącej konkretnej implementacji wspólnej abstrakcyjnej klasy/cechy. To znaczy. mają 'listę klasy abstrakcyjnej' i' cechę Mod rozszerza listę'. Nie możesz po prostu napisać 'class Foo extends List with Mod'. Najpierw musisz mieć konkretną listę, np. 'Klasa LinkedList rozszerza List', możesz napisać' class Bar extends LinkedList with Mod'. Mam nadzieję, że ma to sens. –

+0

Zapomniałem dodać, że dotyczy to tylko cech z metodami "abstrakcyjnego pomijania", aby było jasne. Bez takich metod wydaje się, że cechy można dowolnie mieszać. –