Typ własny dla cechy A
:
trait B
trait A { this: B => }
mówi, że „ A
nie można mieszać w konkretną klasę, która również się nie rozszerza B
” .
Z drugiej strony, następujące:
trait B
trait A extends B
mówi, że „dowolne (konkretne lub abstrakcyjne) mieszanie A
będzie również mieszać w B” .
Czy te dwa stwierdzenia nie oznaczają tego samego? Typ własny wydaje się służyć jedynie do stworzenia możliwości wystąpienia prostego błędu czasu kompilacji.
czego mi brakuje?
trait A[Self] {this: Self => }
jest legalne,trait A[Self] extends Self
nie jest.Odpowiedzi:
Jest używany głównie do wstrzykiwania zależności , na przykład we wzorze ciasta. Istnieje świetny artykuł obejmujący wiele różnych form wstrzykiwania zależności w Scali, w tym wzorzec Cake. Jeśli użyjesz Google „Cake Pattern and Scala”, otrzymasz wiele linków, w tym prezentacje i filmy. Na razie tutaj jest link do innego pytania .
Co do różnicy między typem własnym a rozszerzeniem cechy, to jest proste. Jeśli powiesz
B extends A
, toB
jestA
. Podczas korzystania z self-typy,B
wymagaA
. Istnieją dwa specyficzne wymagania, które są tworzone przy użyciu typów własnych:B
zostanie przedłużony, to jesteś zobowiązany do mix-w sposóbA
.A
.Rozważ następujące przykłady:
Gdyby
Tweeter
była podklasąUser
, nie byłoby błędu. W powyższym kodzie wymagaliśmy użyciaUser
za każdym razemTweeter
, aleUser
nie było to przewidzianeWrong
, więc wystąpił błąd. Teraz, gdy powyższy kod jest nadal w zasięgu, rozważ:Dzięki
Right
spełniony jest wymóg mieszaniaUser
. Jednak drugi wymóg wspomniany powyżej nie jest spełniony: ciężar wdrażaniaUser
wciąż spoczywa na klasach / cechach, które się rozciągająRight
.Ze
RightAgain
oba wymagania są spełnione. AUser
i implementacjaUser
są zapewnione.Więcej praktycznych przypadków użycia znajdują się na początku tej odpowiedzi! Ale mam nadzieję, że teraz to rozumiesz.
źródło
trait WarmerComponentImpl extends SensorDeviceComponent with OnOffDeviceComponent
? To spowodowałobyWarmerComponentImpl
posiadanie tych interfejsów. Będą one dostępne do niczego, że przedłużonyWarmerComponentImpl
, co jest wyraźnie nie tak, jak to jest nieSensorDeviceComponent
, aniOnOffDeviceComponent
. Zależności te są dostępne tylko dla siebieWarmerComponentImpl
. AList
może być użyte jakoArray
i odwrotnie. Ale po prostu to nie to samo.this
z autoportretów jest czymś, na co patrzę z góry, ponieważ cienie bez wyraźnego powodu oryginałuthis
.self: Dep1 with Dep2 =>
.Typy własne pozwalają definiować zależności cykliczne. Na przykład możesz to osiągnąć:
Korzystanie z dziedziczenia
extends
nie pozwala na to. Próbować:W książce Odersky spójrz na rozdział 33.5 (Tworzenie interfejsu użytkownika arkusza kalkulacyjnego), w którym wspomniano:
Mam nadzieję że to pomoże.
źródło
Dodatkową różnicą jest to, że typy własne mogą określać typy nieklasowe. Na przykład
Typ własny jest tutaj typem strukturalnym. Efektem jest powiedzenie, że wszystko, co miesza się w Foo, musi implementować jednostkę zwracającą metodę „bez” argumentu „zamknij”. Pozwala to na bezpieczne miksy do pisania na kaczkach.
źródło
abstract class A extends {def close:Unit}
jest równoważne zabstract class A {def close:Unit}
. Nie dotyczy to typów konstrukcyjnych.Sekcja 2.3 „Adnotacje o typie własnym” oryginalnego artykułu Scali Scalable Component Abstractions Martina Odersky'ego faktycznie bardzo dobrze wyjaśnia cel tego rodzaju poza kompozycją mixin: zapewnia alternatywny sposób kojarzenia klasy z typem abstrakcyjnym.
Przykład podany w artykule był następujący i nie wydaje się, aby miał elegancki korespondent podklasy:
źródło
Kolejna rzecz, o której nie wspomniano: ponieważ typy własne nie są częścią hierarchii wymaganej klasy, można je wykluczyć z dopasowywania wzorców, szczególnie gdy wyczerpuje się dopasowanie do zapieczętowanej hierarchii. Jest to wygodne, gdy chcesz modelować zachowania ortogonalne, takie jak:
źródło
TL; DR streszczenie pozostałych odpowiedzi:
Rozszerzone typy są narażone na odziedziczone typy, ale typy własne nie
np .:
class Cow { this: FourStomachs }
umożliwia stosowanie metod dostępnych tylko dla przeżuwaczy, takich jakdigestGrass
. Jednak cechy, które przedłużają Krowę, nie będą miały takich przywilejów. Z drugiej stronyclass Cow extends FourStomachs
narazidigestGrass
każdego, ktoextends Cow
.samospecjalizacje pozwalają na cykliczne zależności, rozszerzanie innych typów nie
źródło
Zacznijmy od zależności cyklicznej.
Jednak modułowość tego rozwiązania nie jest tak duża, jak mogłoby się wydawać na początku, ponieważ można zastąpić typy własne w następujący sposób:
Chociaż, jeśli zastąpisz członka typu własnego, utracisz dostęp do oryginalnego członka, do którego nadal można uzyskać dostęp poprzez super używanie dziedziczenia. Tak więc to, co naprawdę zyskuje się dzięki dziedziczeniu, to:
Teraz nie mogę twierdzić, że rozumiem wszystkie subtelności wzoru ciasta, ale uderza mnie to, że główną metodą egzekwowania modułowości jest kompozycja, a nie dziedziczenie lub samokształcenie.
Wersja dziedziczenia jest krótsza, ale głównym powodem, dla którego wolę dziedziczenie nad typami własnymi, jest to, że o wiele trudniej jest uzyskać prawidłową kolejność inicjowania z typami własnymi. Są jednak pewne rzeczy, które możesz zrobić z typami siebie, których nie możesz zrobić z dziedziczeniem. Typy własne mogą używać typu, podczas gdy dziedziczenie wymaga cechy lub klasy, jak w:
Możesz nawet zrobić:
Chociaż nigdy nie będziesz w stanie tego zrobić. Nie widzę żadnego bezwzględnego powodu, aby nie móc dziedziczyć po typie, ale z pewnością uważam, że użyteczne byłoby posiadanie klas i cech konstruktora ścieżki, ponieważ mamy cechy / klasy konstruktora typów. Jak niestety
Mamy to:
Albo to:
Jednym z punktów, który należy bardziej wczuć w empatię, jest to, że cechy mogą rozszerzać klasy. Dziękujemy Davidowi Maclverowi za zwrócenie na to uwagi. Oto przykład z mojego własnego kodu:
ScnBase
dziedziczy z klasy Swing Frame, więc może być używany jako własny typ, a następnie dodawany na końcu (w instancji). Jednakval geomR
musi zostać zainicjowany, zanim zostanie wykorzystany przez dziedziczenie cech. Potrzebujemy więc klasy, aby wymusić wcześniejszą inicjalizacjęgeomR
. KlasaScnVista
może być następnie odziedziczona po wielu cechach ortogonalnych, z których można odziedziczyć. Korzystanie z wielu typów parametrów (generycznych) oferuje alternatywną formę modułowości.źródło
źródło
Typ własny pozwala określić, jakie typy mogą mieszać cechę. Na przykład, jeśli masz cechę typu własnego
Closeable
, to ta cecha wie, że jedyne rzeczy, które można mieszać, muszą implementowaćCloseable
interfejs.źródło
trait A { self:B => ... }
to deklaracjaX with A
jest ważna tylko wtedy, gdy X rozszerza B. Tak, możesz powiedziećX with A with Q
, gdzie Q nie rozszerza B, ale uważam, że kikibobo miał na myśli to, że X jest tak ograniczony. A może coś przeoczyłem?Aktualizacja: Zasadnicza różnica polega na tym, że typy własne mogą zależeć od wielu klas (przyznaję, że to trochę przypadek). Na przykład możesz mieć
Pozwala to dodać
Employee
mixin tylko do wszystkiego, co jest podklasąPerson
iExpense
. Oczywiście ma to znaczenie tylko wtedy, gdyExpense
rozszerza sięPerson
i na odwrót. Chodzi o to, że używanie typów własnychEmployee
może być niezależne od hierarchii klas, od których zależy. Nie dba o to, co się rozszerza - jeśli zmienisz hierarchięExpense
vsPerson
, nie musisz modyfikowaćEmployee
.źródło
w pierwszym przypadku pod cechę lub podklasę B można łączyć z dowolnymi zastosowaniami A. Zatem B może być cechą abstrakcyjną.
źródło