Wiem, że dziedziczenie wielokrotne nie jest dozwolone w Javie i C #. Wiele książek po prostu mówi, że wielokrotne dziedziczenie jest niedozwolone. Ale można to zaimplementować za pomocą interfejsów. Nic nie jest omawiane na temat tego, dlaczego jest to niedozwolone. Czy ktoś może mi dokładnie powiedzieć, dlaczego jest to zabronione?
c#
java
language-design
multiple-inheritance
Abdulsattar Mohammed
źródło
źródło
Odpowiedzi:
Krótka odpowiedź brzmi: ponieważ projektanci języka zdecydowali się tego nie robić.
Zasadniczo wydawało się, że zarówno projektanci .NET, jak i Java nie zezwalali na wielokrotne dziedziczenie, ponieważ uważali, że dodanie MI spowodowało zbyt dużą złożoność języków, a jednocześnie zapewniało zbyt małe korzyści .
Aby uzyskać ciekawszą i bardziej dogłębną lekturę, w Internecie dostępnych jest kilka artykułów zawierających wywiady z niektórymi projektantami języka. Na przykład w przypadku .NET Chris Brumme (który pracował w MS nad CLR) wyjaśnił powody, dla których zdecydowali się nie:
Tutaj możesz przeczytać pełny artykuł.
W przypadku języka Java możesz przeczytać ten artykuł :
źródło
Wielokrotne dziedziczenie implementacji jest niedozwolone.
Problem polega na tym, że kompilator / środowisko wykonawcze nie może dowiedzieć się, co zrobić, jeśli masz klasę Cowboy i Artist, obie z implementacjami metody draw (), a następnie próbujesz utworzyć nowy typ CowboyArtist. Co się dzieje, gdy wywołujesz metodę draw ()? Czy ktoś leży martwy na ulicy, czy masz śliczną akwarelę?
Uważam, że nazywa się to problemem dziedziczenia podwójnego diamentu.
źródło
Powód: Java jest bardzo popularna i łatwa do kodowania ze względu na swoją prostotę.
Więc to, co kiedykolwiek programiści Java wydają się trudne do zrozumienia dla programistów, starali się tego uniknąć. Jednym z takich rodzajów własności jest dziedziczenie wielokrotne.
Problem z dziedziczeniem wielokrotnym: problem z diamentami.
Przykład :
To jest dwuznaczność istniejąca w problemie diamentów.
Nie jest niemożliwe rozwiązanie tego problemu, ale powoduje to większe zamieszanie i złożoność dla programisty podczas czytania. Powoduje więcej problemów niż próbuje rozwiązać.
Uwaga : ale zawsze możesz pośrednio zaimplementować wielokrotne dziedziczenie za pomocą interfejsów.
źródło
Ponieważ Java ma znacznie inną filozofię projektowania niż C ++. (Nie będę tutaj omawiać języka C #).
Projektując C ++, Stroustrup chciał uwzględnić przydatne funkcje, niezależnie od tego, w jaki sposób można je niewłaściwie wykorzystać. Możliwe jest zepsucie wielu rzeczy z wielokrotnym dziedziczeniem, przeciążeniem operatorów, szablonami i różnymi innymi funkcjami, ale można też zrobić z nimi kilka bardzo dobrych rzeczy.
Filozofia projektowania w Javie polega na podkreśleniu bezpieczeństwa w konstrukcjach językowych. W rezultacie istnieją rzeczy, które są o wiele trudniejsze do wykonania, ale możesz być dużo bardziej pewny, że kod, na który patrzysz, oznacza to, co myślisz, że robi.
Co więcej, Java była w dużej mierze reakcją C ++ i Smalltalk, najbardziej znanych języków obiektowych. Istnieje wiele innych języków obiektowych (Common Lisp był właściwie pierwszym znormalizowanym językiem), z różnymi systemami obiektowymi, które lepiej obsługują MI.
Nie wspominając już o tym, że jest całkowicie możliwe wykonanie MI w Javie, używając interfejsów, kompozycji i delegowania. Jest bardziej wyraźny niż w C ++ i dlatego jest trudniejszy w użyciu, ale da ci coś, co prawdopodobnie zrozumiesz na pierwszy rzut oka.
Nie ma tutaj właściwej odpowiedzi. Odpowiedzi są różne, a to, która z nich jest lepsza w danej sytuacji, zależy od aplikacji i indywidualnych preferencji.
źródło
Głównym (choć nie jedynym) powodem, dla którego ludzie odwracają się od MI, jest tak zwany „problem diamentów” prowadzący do niejasności w implementacji. Ten artykuł na Wikipedii omawia to i wyjaśnia lepiej niż ja. MI może również prowadzić do bardziej złożonego kodu, a wielu projektantów OO twierdzi, że nie potrzebujesz MI, a jeśli go używasz, prawdopodobnie twój model jest zły. Nie jestem pewien, czy zgadzam się z tym ostatnim punktem, ale prostota jest zawsze dobrym planem.
źródło
W C ++ wielokrotne dziedziczenie było głównym problemem, gdy zostało użyte nieprawidłowo. Aby uniknąć tych popularnych problemów projektowych, we współczesnych językach (java, C #) wymuszono „dziedziczenie” wielu interfejsów.
źródło
Dziedziczenie wielokrotne to
Dlatego rozsądnym wyborem może być nie uwzględnianie wielokrotnego dziedziczenia w języku Java.
źródło
Innym powodem jest to, że dziedziczenie pojedyncze sprawia, że rzutowanie staje się trywialne, nie emitując instrukcji asemblera (poza sprawdzaniem zgodności typów, jeśli jest to wymagane). Gdybyś miał dziedziczenie wielokrotne, musiałbyś dowiedzieć się, gdzie w klasie podrzędnej zaczyna się dany rodzic. Zatem wydajność jest z pewnością zaletą (choć nie jedyną).
źródło
W dawnych czasach (lata 70.), kiedy informatyka była bardziej naukowa i mniej masowa, programiści mieli czas na zastanowienie się nad dobrym projektem i dobrym wdrożeniem, w wyniku czego produkty (programy) charakteryzowały się wysoką jakością (np. Projekt TCP / IP) i wdrożenie). W dzisiejszych czasach, kiedy wszyscy programują, a menedżerowie zmieniają specyfikacje przed terminami, subtelne kwestie, takie jak te opisane w linku do Wikipedii z postu Steve'a Haigha, są trudne do śledzenia; dlatego „dziedziczenie wielokrotne” jest ograniczone konstrukcją kompilatora. Jeśli Ci się spodoba, nadal możesz używać C ++ ... i mieć pełną swobodę :)
źródło
Stwierdzenie, że „dziedziczenie wielokrotne nie jest dozwolone w Javie”, przyjmuję z przymrużeniem oka.
Dziedziczenie wielokrotne jest definiowane, gdy „Typ” dziedziczy z więcej niż jednego „Typu”. Interfejsy są również klasyfikowane jako typy, ponieważ mają zachowanie. Tak więc Java ma dziedziczenie wielokrotne. Tylko tyle, że jest bezpieczniejsze.
źródło
Dynamiczne ładowanie klas utrudnia implementację dziedziczenia wielokrotnego.
W Javie faktycznie uniknęli złożoności wielokrotnego dziedziczenia, zamiast tego wykorzystując pojedyncze dziedziczenie i interfejs. Złożoność wielokrotnego dziedziczenia jest bardzo duża w sytuacji opisanej poniżej
diamentowy problem wielokrotnego dziedziczenia. Mamy dwie klasy B i C dziedziczące po A. Załóżmy, że B i C przesłaniają odziedziczoną metodę i zapewniają własną implementację. Teraz D dziedziczy zarówno z B, jak i C, wykonując dziedziczenie wielokrotne. D powinien dziedziczyć tę zastąpioną metodę, jvm nie może zdecydować, która zastąpiona metoda zostanie użyta?
W języku c ++ funkcje wirtualne są używane do obsługi i musimy to zrobić jawnie.
Można tego uniknąć, używając interfejsów, nie ma treści metod. Nie można utworzyć instancji interfejsów - można je zaimplementować tylko przez klasy lub rozszerzyć o inne interfejsy.
źródło
W rzeczywistości dziedziczenie wielokrotne będzie skomplikowane, jeśli odziedziczone klasy mają tę samą funkcję. tj. kompilator będzie miał zamieszanie, które trzeba wybrać (problem z diamentami). Tak więc w Javie ta złożoność usunęła się i dała interfejs, aby uzyskać funkcjonalność taką jak wielokrotne dziedziczenie. Możemy skorzystać z interfejsu
źródło
Java ma koncept, czyli polimorfizm. W Javie istnieją 2 rodzaje polimorfizmu. Istnieje przeciążanie metod i zastępowanie metod. Wśród nich nadpisywanie metody odbywa się z relacją nad- i podklasową. Jeśli tworzymy obiekt podklasy i wywołujemy metodę nadklasy, a podklasa rozszerza więcej niż jedną klasę, jaką metodę superklasy należy wywołać?
Lub podczas wywoływania konstruktora nadklasy przez
super()
, który konstruktor superklasy zostanie wywołany?Takie decyzje są niemożliwe ze względu na obecne funkcje Java API. więc dziedziczenie wielokrotne nie jest dozwolone w java.
źródło
Dziedziczenie wielokrotne nie jest dozwolone bezpośrednio w Javie, ale jest dozwolone przez interfejsy.
Powód:
Dziedziczenie wielokrotne: wprowadza większą złożoność i niejednoznaczność.
Interfejsy: interfejsy są całkowicie abstrakcyjnymi klasami w Javie, które zapewniają jednolity sposób prawidłowego określenia struktury lub wewnętrznego działania programu z poziomu publicznie dostępnego interfejsu, czego konsekwencją jest większa elastyczność i kod wielokrotnego użytku, a także większa kontrola nad sposobem tworzenia innych klas i interakcji z nimi.
Mówiąc dokładniej, są one specjalną konstrukcją w Javie z dodatkową cechą, która umożliwia wykonywanie pewnego rodzaju dziedziczenia wielokrotnego, tj. Klas, które można upcastować do więcej niż jednej klasy.
Weźmy prosty przykład.
Załóżmy, że istnieją 2 nadklasy klasy A i B z tymi samymi nazwami metod, ale różnymi funkcjami. Poprzez następujący kod ze słowem kluczowym (extends) nie jest możliwe wielokrotne dziedziczenie.
Ale poprzez interfejsy, ze słowem kluczowym (implements) możliwe jest wielokrotne dziedziczenie.
źródło
Możesz znaleźć odpowiedź, korzystając z tego łącza do dokumentacji
Jeśli wielokrotne dziedziczenie jest dozwolone i podczas tworzenia obiektu przez utworzenie instancji tej klasy, ten obiekt odziedziczy pola ze wszystkich superklas tej klasy. Spowoduje to dwa problemy.
Co się stanie, jeśli metody lub konstruktory z różnych superklas utworzą instancję tego samego pola?
Która metoda lub konstruktor będzie mieć pierwszeństwo?
Mimo że wielokrotne dziedziczenie stanu jest teraz dozwolone, nadal można zaimplementować
Wielokrotne dziedziczenie typu : zdolność klasy do implementacji więcej niż jednego interfejsu.
Wielokrotne dziedziczenie implementacji (poprzez domyślne metody w interfejsach): Możliwość dziedziczenia definicji metod z wielu klas
Dodatkowe informacje można znaleźć w tym powiązanym pytaniu SE:
Wielokrotna niejednoznaczność dziedziczenia z interfejsem
źródło
W C ++ klasa może dziedziczyć (bezpośrednio lub pośrednio) z więcej niż jednej klasy, co jest określane jako wielokrotne dziedziczenie .
Jednak języki C # i Java ograniczają klasy do pojedynczego dziedziczenia, które każda klasa dziedziczy z jednej klasy nadrzędnej.
Dziedziczenie wielokrotne to przydatny sposób tworzenia klas, które łączą aspekty dwóch różnych hierarchii klas, co często się zdarza, gdy używa się różnych struktur klas w jednej aplikacji.
Jeśli dwie struktury definiują własne klasy bazowe dla wyjątków, na przykład można użyć dziedziczenia wielokrotnego, aby utworzyć klasy wyjątków, które mogą być używane z dowolną strukturą.
Problem z dziedziczeniem wielokrotnym polega na tym, że może prowadzić do niejednoznaczności. Klasycznym przykładem jest sytuacja, gdy klasa dziedziczy po dwóch innych klasach, z których każda dziedziczy po tej samej klasie:
W tym przykładzie element
flag
członkowski danych jest zdefiniowany przezclass A
. Aleclass D
pochodzi odclass B
iclass C
, które wywodzą się z obuA
, a więc w istocie dwie kopie zflag
są dostępne, ponieważ dwa przypadkiA
są wD
„s klasowej hierarchii. Który chcesz ustawić? Kompilator będzie narzekał, że odwołanie doflag
inD
jest niejednoznaczne . Jedną z poprawek jest jawne ujednoznacznienie odniesienia:Innym rozwiązaniem jest zadeklarowanie B i C jako
virtual base classes
, co oznacza, że tylko jedna kopia A może istnieć w hierarchii, eliminując wszelkie niejednoznaczności.Istnieją inne zawiłości związane z dziedziczeniem wielokrotnym, na przykład kolejność inicjowania klas podstawowych podczas konstruowania obiektu pochodnego lub sposób, w jaki elementy członkowskie mogą być nieumyślnie ukrywane w klasach pochodnych. Aby uniknąć tych zawiłości, niektóre języki ograniczają się do prostszego pojedynczego modelu dziedziczenia.
Chociaż znacznie upraszcza to dziedziczenie, ogranicza również jego użyteczność, ponieważ tylko klasy mające wspólnego przodka mogą dzielić zachowania. Interfejsy nieco łagodzą to ograniczenie, umożliwiając klasom w różnych hierarchiach ujawnianie wspólnych interfejsów, nawet jeśli nie są one implementowane przez udostępnianie kodu.
źródło
Wyobraź sobie ten przykład: mam klasę
Shape1
Ma
CalcualteArea
metodę:Jest inna klasa,
Shape2
która również ma tę samą metodęTeraz mam podrzędną klasę Circle, która wywodzi się zarówno z Shape1, jak i Shape2;
Teraz, kiedy tworzę obiekt dla Circle i wywołuję tę metodę, system nie wie, którą metodę obliczania powierzchni należy wywołać. Obie mają takie same podpisy. Więc kompilator będzie się mylić. Dlatego wielokrotne dziedziczenie nie jest dozwolone.
Ale może być wiele interfejsów, ponieważ interfejsy nie mają definicji metody. Nawet oba interfejsy mają tę samą metodę, oba nie mają żadnej implementacji i zawsze zostanie wykonana metoda w klasie potomnej.
źródło