W jednym z moich wywiadów poproszono mnie o wyjaśnienie różnicy między klasą interfejsu a klasą abstrakcyjną .
Oto moja odpowiedź:
Metody interfejsu Java są domyślnie abstrakcyjne i nie mogą mieć implementacji. Klasa abstrakcyjna Java może mieć metody instancji, które implementują domyślne zachowanie.
Zmienne zadeklarowane w interfejsie Java są domyślnie ostateczne. Klasa abstrakcyjna może zawierać nie-końcowe zmienne.
Członkowie interfejsu Java są domyślnie publiczni. Klasa abstrakcyjna Java może mieć zwykłe smaki członków klasy, takie jak prywatny, chroniony itp.
Interfejs Java powinien zostać zaimplementowany przy użyciu słowa kluczowego „implements”; Klasa abstrakcyjna Java powinna zostać rozszerzona za pomocą słowa kluczowego „extends”.
Interfejs może rozszerzyć tylko inny interfejs Java, klasa abstrakcyjna może rozszerzyć inną klasę Java i zaimplementować wiele interfejsów Java.
Klasa Java może implementować wiele interfejsów, ale może rozszerzyć tylko jedną klasę abstrakcyjną.
Ankieter nie był jednak usatysfakcjonowany i powiedział mi, że ten opis reprezentuje „ wiedzę książkową ”.
Poprosił mnie o bardziej praktyczną odpowiedź, wyjaśniając, kiedy wybiorę klasę abstrakcyjną zamiast interfejsu, używając praktycznych przykładów .
Gdzie popełniłem błąd?
źródło
Odpowiedzi:
Najpierw dam wam przykład:
Załóżmy, że masz 3 bazy danych w swojej aplikacji. Następnie każda implementacja dla tej bazy danych musi zdefiniować powyższe 2 metody:
Ale co jeśli
encryptPassword()
nie jest zależny od bazy danych i jest taki sam dla każdej klasy? Zatem powyższe podejście nie byłoby dobrym podejściem.Zamiast tego rozważ to podejście:
Teraz w każdej klasie potomnej musimy zaimplementować tylko jedną metodę - metodę zależną od bazy danych.
źródło
Na tym świecie nic nie jest idealne. Być może oczekiwali bardziej praktycznego podejścia.
Ale po wyjaśnieniu możesz dodać te wiersze z nieco innym podejściem.
Interfejsy to reguły (reguły, ponieważ należy je wdrożyć, których nie można zignorować lub których należy unikać, aby były narzucone jak reguły), co działa jako wspólny dokument dla różnych zespołów tworzących oprogramowanie.
Interfejsy dają wyobrażenie o tym, co należy zrobić, ale nie w jaki sposób. Tak więc implementacja całkowicie zależy od programisty, zgodnie z podanymi regułami (oznacza dany podpis metod).
Klasy abstrakcyjne mogą zawierać deklaracje abstrakcyjne, konkretne implementacje lub oba.
Deklaracje abstrakcyjne są jak reguły, których należy przestrzegać, a konkretne implementacje są jak wytyczne (możesz użyć go takim, jaki jest, lub możesz go zignorować, zastępując go i nadając mu własną implementację).
Ponadto, które metody z tą samą sygnaturą mogą zmienić zachowanie w różnych kontekstach, są dostarczane jako deklaracje interfejsu jako reguły do odpowiedniej implementacji w różnych kontekstach.
Edycja: Java 8 ułatwia zdefiniowanie domyślnych i statycznych metod w interfejsie.
Teraz, gdy klasa zaimplementuje SomeInterface, nie jest obowiązkowe zapewnienie implementacji domyślnych metod interfejsu.
Jeśli mamy inny interfejs z następującymi metodami:
Java nie pozwala na rozszerzanie wielu klas, ponieważ powoduje to „problem diamentowy”, w którym kompilator nie jest w stanie zdecydować, której metody nadklasy użyć. W przypadku metod domyślnych problem z diamentem pojawi się również w przypadku interfejsów. Ponieważ jeśli klasa implementuje oba
i nie implementuje wspólnej domyślnej metody, kompilator nie może zdecydować, który wybrać. Aby uniknąć tego problemu, w java 8 obowiązkowe jest wdrożenie typowych domyślnych metod różnych interfejsów. Jeśli jakakolwiek klasa implementuje oba powyższe interfejsy, musi zapewnić implementację metody defaultMethod (), w przeciwnym razie kompilator zgłosi błąd czasu kompilacji.
źródło
Zrobiłeś dobre podsumowanie praktycznych różnic w użyciu i implementacji, ale nie powiedziałeś nic o różnicy w znaczeniu.
interfejs jest opis zachowania klasa wykonawczy będzie musiał. Klasa implementacyjna zapewnia, że będzie miała te metody, których można na niej użyć. Zasadniczo jest to umowa lub obietnica złożona przez klasę.
Klasa abstrakcyjna jest podstawą dla różnych podklas że zachowanie zakładowego, który nie muszą być wielokrotnie tworzone. Podklasy muszą uzupełnić zachowanie i mieć możliwość zastąpienia predefiniowanego zachowania (o ile nie jest zdefiniowane jako
final
lubprivate
).Znajdziesz dobre przykłady w
java.util
pakiecie, który zawiera interfejsy podobneList
i klasy abstrakcyjne,AbstractList
które już implementują interfejs. Oficjalna dokumentacja opisujeAbstractList
następująco:źródło
abstract
słowie kluczowym dotyczącym słowa kluczowego, to znaczy, gdy kompilator to widzi, wiedzą, że następujące informacje są niekompletne i wymagają implementacji . Interfejsy są zawsze niekompletne, ale klasy abstrakcyjne są abstrakcyjne, ponieważ musiały miećincomplete (abstract)
metody.Interfejs składa się ze zmiennych singletonowych (public static final) i publicznych metod abstrakcyjnych. Zwykle wolimy korzystać z interfejsu w czasie rzeczywistym, gdy wiemy, co robić, ale nie wiemy, jak to zrobić .
Pojęcie to można lepiej zrozumieć na przykładzie:
Rozważ klasę płatności. Płatności można dokonać na wiele sposobów, takich jak PayPal, karta kredytowa itp. Dlatego zazwyczaj bierzemy Payment jako nasz interfejs zawierający
makePayment()
metodę, a CreditCard i PayPal to dwie klasy implementacji.W powyższym przykładzie CreditCard i PayPal to dwie klasy / strategie implementacji. Interfejs pozwala nam również na koncepcję wielokrotnego dziedziczenia w Javie, której nie można osiągnąć za pomocą klasy abstrakcyjnej.
Wybieramy klasę abstrakcyjną, gdy istnieją pewne funkcje, dla których wiemy, co robić, i inne funkcje, które wiemy, jak wykonać .
Rozważ następujący przykład:
Jeśli w przyszłości dodamy metody (konkretne / abstrakcyjne) do danej klasy abstrakcyjnej, klasa implementacyjna nie będzie wymagać zmiany jej kodu. Jeśli jednak w przyszłości dodamy metody do interfejsu, musimy dodać implementacje do wszystkich klas, które zaimplementowały ten interfejs, w przeciwnym razie wystąpią błędy czasu kompilacji.
Istnieją inne różnice, ale są to główne, które mogły być takie, jakich oczekiwał Twój ankieter. Mam nadzieję, że było to pomocne.
źródło
interface
iabstract class
.Różnica między klasą Abstact a interfejsem
Domyślne metody interfejsu w Javie 8
Metoda statyczna interfejsu Java
Interfejsy funkcjonalne Java
Klasy abstrakcyjne a interfejsy w Javie 8
Różnica koncepcyjna:
Klasy abstrakcyjne są poprawne dla szkieletowych (tj. Częściowych) implementacji interfejsów, ale nie powinny istnieć bez pasującego interfejsu.
Więc kiedy klasy abstrakcyjne zostaną skutecznie zredukowane do niewidoczności, szkieletowe implementacje interfejsów, czy metody domyślne również to zabiorą? Zdecydowanie: nie! Implementacja interfejsów prawie zawsze wymaga niektórych lub wszystkich narzędzi do budowania klas, których brakuje metod domyślnych. A jeśli jakiś interfejs tego nie robi, jest to wyraźnie przypadek szczególny, który nie powinien cię sprowadzać na manowce.
Domyślne metody interfejsu w Javie 8
Java 8 wprowadza „ metodę domyślną nową funkcję ” lub (metody Defender), która pozwala programistom dodawać nowe metody do interfejsów bez przerywania istniejącej implementacji tych interfejsów. Zapewnia elastyczność umożliwiającą implementację interfejsu, która będzie domyślnie używana w sytuacji, gdy konkretna klasa nie zapewni implementacji dla tej metody.
Rozważmy mały przykład, aby zrozumieć, jak to działa:
Następująca klasa pomyślnie skompiluje się w Javie JDK 8,
Jeśli utworzysz instancję OldInterfaceImpl:
Metoda domyślna:
Do interfejsu można podać domyślne metody bez wpływu na implementację klas, ponieważ obejmuje on implementację. Jeśli każda dodana metoda w interfejsie zdefiniowanym z implementacją nie wpłynie na żadną klasę implementującą. Klasa implementująca może zastąpić domyślną implementację zapewnianą przez interfejs.
Po rozszerzeniu interfejsu zawierającego metodę domyślną możemy wykonać następujące czynności,
Błąd kompilacji metody ForEach rozwiązany przy użyciu metody domyślnej
W Javie 8 kolekcje JDK zostały rozszerzone, a metoda forEach została dodana do całej kolekcji (która działa w połączeniu z lambdami). W tradycyjny sposób kod wygląda jak poniżej,
Ponieważ wynik ten powoduje, że każda klasa implementująca ma błędy kompilacji, dodano domyślną metodę z wymaganą implementacją, aby istniejąca implementacja nie została zmieniona.
Interfejs Iterable z metodą domyślną znajduje się poniżej,
Ten sam mechanizm został użyty do dodania strumienia w interfejsie JDK bez przerywania klas implementujących.
Metoda domyślna i problemy z wieloznacznością dziedziczenia wielokrotnego
Ponieważ klasa Java może implementować wiele interfejsów, a każdy interfejs może zdefiniować domyślną metodę z tą samą sygnaturą metody, dlatego odziedziczone metody mogą ze sobą kolidować.
Rozważ poniższy przykład,
Powyższy kod nie zostanie skompilowany z następującym błędem,
Aby naprawić tę klasę, musimy zapewnić domyślną implementację metody:
Ponadto, jeśli chcemy wywołać domyślną implementację zapewnianą przez którykolwiek z super interfejsów, a nie naszą własną implementację, możemy to zrobić w następujący sposób,
Możemy wybrać dowolną domyślną implementację lub obie w ramach naszej nowej metody.
Ważne informacje na temat domyślnych metod interfejsu Java:
Link do zasobu:
Metoda statyczna interfejsu Java
Metoda statyczna interfejsu Java, przykład kodu, metoda statyczna a metoda domyślna
Metoda statyczna interfejsu Java jest podobna do metody domyślnej, z tym wyjątkiem, że nie możemy ich przesłonić w klasach implementacji. Ta funkcja pomaga nam uniknąć niepożądanych wyników w przypadku złej implementacji w klasach implementacyjnych. Spójrzmy na to na prostym przykładzie.
Zobaczmy teraz klasę implementacji, która ma metodę isNull () ze słabą implementacją.
Zauważ, że isNull (String str) to prosta metoda klasy, nie zastępująca metody interfejsu. Na przykład, jeśli dodamy adnotację @Override do metody isNull (), spowoduje to błąd kompilatora.
Teraz, kiedy uruchomimy aplikację, otrzymamy następujące dane wyjściowe.
Jeśli zmienimy metodę interfejsu ze statycznej na domyślną, otrzymamy następujące dane wyjściowe.
Metoda statyczna interfejsu Java jest widoczna tylko dla metod interfejsu, jeśli usuniemy metodę isNull () z klasy MyDataImpl, nie będziemy mogli jej użyć do obiektu MyDataImpl. Jednak podobnie jak inne metody statyczne, możemy używać metod statycznych interfejsu przy użyciu nazwy klasy. Na przykład poprawna instrukcja to:
Ważne informacje dotyczące statycznej metody interfejsu Java:
Interfejsy funkcjonalne Java
Zanim zakończę ten post, chciałbym krótko przedstawić interfejsy funkcjonalne. Interfejs z dokładnie jedną metodą abstrakcyjną jest znany jako interfejs funkcjonalny.
Wprowadzono nową adnotację
@FunctionalInterface
, aby oznaczyć interfejs jako interfejs funkcjonalny.@FunctionalInterface
adnotacja to funkcja pozwalająca uniknąć przypadkowego dodania metod abstrakcyjnych w interfejsach funkcjonalnych. Korzystanie z niego jest opcjonalne, ale dobrą praktyką.Interfejsy funkcjonalne są długo wyczekiwaną i bardzo poszukiwaną funkcją Java 8, ponieważ pozwala nam używać wyrażeń lambda do ich tworzenia. Dodano nowy pakiet java.util.function z szeregiem funkcjonalnych interfejsów, aby zapewnić typy docelowe dla wyrażeń lambda i odwołań do metod. W przyszłych postach przyjrzymy się interfejsom funkcjonalnym i wyrażeniom lambda.
Lokalizacja zasobu:
źródło
Wszystkie twoje instrukcje są ważne z wyjątkiem pierwszej instrukcji (po wydaniu Java 8):
Z dokumentacji stronie :
Metody domyślne:
Interfejs może mieć domyślne metody , ale różni się od metod abstrakcyjnych w klasach abstrakcyjnych.
Po rozszerzeniu interfejsu zawierającego metodę domyślną możesz wykonać następujące czynności:
abstract
.Metody statyczne:
Oprócz metod domyślnych można definiować metody statyczne w interfejsach. (Metoda statyczna to metoda powiązana z klasą, w której jest zdefiniowana, a nie z dowolnym obiektem. Każda instancja klasy ma wspólne metody statyczne).
Ułatwia to organizowanie metod pomocniczych w bibliotekach;
Przykładowy kod ze strony dokumentacji o
interface
posiadaniustatic
idefault
metodach.Skorzystaj z poniższych wskazówek, aby wybrać interfejs lub klasę abstrakcyjną.
Berło:
Klasa abstrakcyjna:
Współdziel kod między kilkoma blisko powiązanymi klasami. Ustanawia ona jest relacja.
Udostępnij wspólny stan między pokrewnymi klasami (stan można zmodyfikować w konkretnych klasach)
Powiązane posty:
Interfejs vs Klasa abstrakcyjna (ogólne OO)
Implementuje vs rozszerza: kiedy używać? Co za różnica?
Przechodząc przez te przykłady, możesz to zrozumieć
Klasy niezwiązane mogą mieć możliwości poprzez interfejs, ale klasy pokrewne zmieniają zachowanie poprzez rozszerzenie klas podstawowych.
źródło
stateless
interfejsu jest miłym hitem. Interfejs nie może mieć żadnego stanu (interfejs może mieć stałe, ale są one ostateczne / statyczne, a zatem niezmienne).Twoje wyjaśnienie wygląda na przyzwoite, ale może wyglądało na to, że czytałeś to wszystko z podręcznika? : - /
Bardziej niepokoi mnie to, jak solidny był twój przykład? Czy starałeś się uwzględnić prawie wszystkie różnice między abstrakcją a interfejsami?
Osobiście sugerowałbym ten link: http://mindprod.com/jgloss/interfacevsabstract.html#TABLE
dla wyczerpującej listy różnic ..
Mam nadzieję, że pomoże to Tobie i wszystkim innym czytelnikom w przyszłych wywiadach
źródło
Wielu młodszych programistów popełnia błąd, uważając interfejsy, klasy abstrakcyjne i konkretne za niewielkie odmiany tego samego, i wybiera jeden z nich wyłącznie ze względów technicznych: Czy potrzebuję wielokrotnego dziedziczenia? Czy potrzebuję miejsca, aby zastosować wspólne metody? Czy muszę zawracać sobie głowę czymś innym niż konkretną klasą? Jest to błędne i ukryty w tych pytaniach jest główny problem: „ja” . Pisząc kod dla siebie, rzadko myślisz o innych obecnych lub przyszłych programistach pracujących nad tym kodem lub z nim.
Interfejsy i klasy abstrakcyjne, choć pozornie podobne z technicznego punktu widzenia, mają zupełnie inne znaczenia i cele.
Podsumowanie
Interfejs definiuje umowę, którą zrealizuje dla Ciebie niektóre wdrożenie .
Klasa abstrakcyjna zapewnia domyślne zachowanie, którego implementacja może ponownie użyć.
Te dwa punkty powyżej są tym, czego szukam podczas wywiadu, i jest to wystarczająco zwięzłe streszczenie. Czytaj dalej, aby uzyskać więcej informacji.
Alternatywne podsumowanie
Przez przykład
Innymi słowy: konkretna klasa wykonuje konkretną pracę w bardzo specyficzny sposób. Na przykład,
ArrayList
wykorzystuje ciągły obszar pamięci do przechowywania listy obiektów w zwarty sposób, który oferuje szybki losowy dostęp, iterację i zmiany w miejscu, ale jest straszny przy wstawianiu, usuwaniu, a czasem nawet dodawaniu; w międzyczasieLinkedList
używa podwójnie połączonych węzłów do przechowywania listy obiektów, która zamiast tego oferuje szybką iterację, zmiany w miejscu oraz wstawianie / usuwanie / dodawanie, ale jest straszna przy przypadkowym dostępie. Te dwa typy list są zoptymalizowane pod kątem różnych przypadków użycia i bardzo ważne jest, jak je wykorzystasz. Kiedy próbujesz wycisnąć wydajność z listy, z którą intensywnie wchodzisz w interakcję, i gdy wybór rodzaju listy zależy od ciebie, powinieneś ostrożnie wybrać, który z nich tworzysz.Z drugiej strony, użytkownicy wysokiego poziomu listy tak naprawdę nie dbają o to, jak jest ona faktycznie implementowana i powinni być odizolowani od tych szczegółów. Wyobraźmy sobie, że Java nie ujawnia
List
interfejsu, ale ma tylko konkretnąList
klasę, która jest właśnie tym, coLinkedList
jest teraz. Wszyscy programiści Java dostosowaliby swój kod do szczegółów implementacji: unikają losowego dostępu, dodają pamięć podręczną, aby przyspieszyć dostęp, lub po prostu dokonują ponownej implementacjiArrayList
samodzielnie, chociaż byłoby to niezgodne z całym innym kodem, który faktycznie działaList
tylko z . To byłoby okropne ... Ale teraz wyobraź sobie, że mistrzowie Javy faktycznie zdają sobie sprawę, że połączona lista jest okropna w większości rzeczywistych przypadków użycia, i postanowili przełączyć się na listę tablic tylko dla siebieList
klasa dostępna. Wpłynęłoby to na wydajność każdego programu Java na świecie i ludzie nie byliby z tego powodu zadowoleni. A głównym winowajcą jest to, że szczegóły implementacji były dostępne, a programiści założyli, że są to stałe umowy, na których mogą polegać. Dlatego ważne jest, aby ukryć szczegóły implementacji i zdefiniować tylko abstrakcyjną umowę. Jest to cel interfejsu: zdefiniowanie, jaki rodzaj danych wejściowych akceptuje metoda i jakiego rodzaju danych wyjściowych oczekuje się, bez ujawniania wszystkich odwagi, która skusiłaby programistów do poprawienia kodu w celu dopasowania do wewnętrznych szczegółów, które mogą ulec zmianie przy każdej przyszłej aktualizacji .Klasa abstrakcyjna znajduje się pośrodku między interfejsami a konkretnymi klasami. Ma to pomóc implementacjom we wspólnym lub nudnym kodzie. Na przykład,
AbstractCollection
zapewnia, że podstawowe implementacjeisEmpty
oparte na rozmiarze sącontains
równe 0, gdy iterują i porównują,addAll
jak powtórzonoadd
itd. Pozwala to implementacjom skoncentrować się na kluczowych elementach, które je odróżniają: jak faktycznie przechowywać i odzyskiwać dane.Inna perspektywa: interfejsy API a interfejsy SPI
Interfejsy są bramami o niskiej spójności między różnymi częściami kodu. Pozwalają one na istnienie i ewolucję bibliotek bez rozbijania każdego użytkownika biblioteki, gdy coś zmienia się wewnętrznie. To się nazywa interfejs programowania aplikacji , a nie klasy programowania aplikacji. Na mniejszą skalę pozwalają także wielu programistom z powodzeniem współpracować przy projektach na dużą skalę, oddzielając różne moduły za pomocą dobrze udokumentowanych interfejsów.
Klasy abstrakcyjne są pomocnikami o wysokiej spójności, które można stosować podczas implementacji interfejsu, przy założeniu pewnego poziomu szczegółów implementacji. Alternatywnie klasy abstrakcyjne są używane do definiowania SPI, interfejsów dostawcy usług.
Różnica między interfejsem API a interfejsem SPI jest subtelna, ale ważna: w przypadku interfejsu API skupia się na tym, kto go używa , aw przypadku interfejsu SPI koncentruje się na tym, kto go implementuje .
Dodawanie metod do interfejsu API jest łatwe, wszyscy obecni użytkownicy interfejsu API będą się nadal kompilować. Dodawanie metod do SPI jest trudne, ponieważ każdy dostawca usług (konkretne wdrożenie) będzie musiał wdrożyć nowe metody. Jeśli interfejsy są używane do definiowania SPI, dostawca będzie musiał wydać nową wersję za każdym razem, gdy zmieni się umowa SPI. Jeśli zamiast tego zostaną użyte klasy abstrakcyjne, nowe metody można zdefiniować w kategoriach istniejących metod abstrakcyjnych lub jako puste
throw not implemented exception
kody pośredniczące, co pozwoli przynajmniej kompilować i uruchamiać starszą wersję implementacji usługi.Uwaga na temat Java 8 i metod domyślnych
Chociaż Java 8 wprowadziła domyślne metody interfejsów, co sprawia, że linia między interfejsami a klasami abstrakcyjnymi jest jeszcze bardziej rozmyta, nie było to tak, że implementacje mogą ponownie wykorzystywać kod, ale aby ułatwić zmianę interfejsów, które służą zarówno jako interfejs API, jak i SPI (lub są niewłaściwie używane do definiowania SPI zamiast klas abstrakcyjnych).
„Wiedza książkowa”
Szczegóły techniczne podane w odpowiedzi PO są uważane za „wiedzę książkową”, ponieważ jest to zwykle podejście stosowane w szkole i w większości książek o technologii na temat języka: co to jest, a nie jak używać go w praktyce, zwłaszcza w aplikacjach na dużą skalę .
Oto analogia: przypuszczano, że pytanie brzmiało:
Odpowiedź techniczna brzmi:
To wszystko prawda, ale całkowicie pomija argumenty, że są to dwie zupełnie różne rzeczy, i obie mogą być używane jednocześnie do różnych celów, a aspekt „robienia tego” nie jest najważniejszy w żadnej z tych dwóch opcji . Odpowiedź nie ma perspektywy, pokazuje niedojrzały sposób myślenia, a jednocześnie poprawnie przedstawia prawdziwe „fakty”.
źródło
A co z myśleniem w następujący sposób:
Więc jeśli masz abstrakcyjną klasę Mammals, podklasę Human i interfejs Driving, możesz powiedzieć
Moja sugestia jest taka, że fraza wiedzy książkowej wskazuje, że chciał usłyszeć semantyczną różnicę między nimi (tak jak inni już tutaj zasugerowali).
źródło
Interfejs to „umowa”, w której klasa implementująca umowę obiecuje wdrożenie metod. Przykładem, w którym musiałem napisać interfejs zamiast klasy, było uaktualnienie gry z 2D do 3D. Musiałem stworzyć interfejs do dzielenia klas między wersją 2D i 3D gry.
Następnie mogę zaimplementować metody oparte na środowisku, wciąż będąc w stanie wywoływać te metody z obiektu, który nie wie, która wersja gry się ładuje.
public class Adventure extends JFrame implements Playable
public class Dungeon3D extends SimpleApplication implements Playable
public class Main extends SimpleApplication implements AnimEventListener, ActionListener, Playable
Zazwyczaj w świecie gry świat może być klasą abstrakcyjną, która wykonuje metody w grze:
źródło
Klasy abstrakcyjne nie są czystą abstrakcją, lecz zbiorem konkretnych (zaimplementowanych metod), jak również metod niezaimplementowanych. Ale interfejsy są czystą abstrakcją, ponieważ istnieją tylko metody niezaimplementowane, a nie konkretne.
Dlaczego klasy abstrakcyjne?
Dlaczego interfejsy?
źródło
interfejs jest jak zestaw genów, które są publicznie udokumentowanych mieć jakiś efekt: DNA badanie powie mi, czy mam je - i jeśli to zrobię, mogę publicznie oświadczyć, że jestem „nośnik ”i część mojego zachowania lub stanu będzie z nimi zgodna. (Ale oczywiście mogę mieć wiele innych genów, które zapewniają cechy poza tym zakresem).
Klasa abstrakcyjna jest jak martwego przodka z niekoedukacyjnego gatunki (*): Ona nie może być powołany do życia, ale życia (czyli non-streszczenie ) potomnych dziedziczy wszystkie jej genów.
(*) Aby rozciągnąć tę metaforę, powiedzmy, że wszyscy członkowie gatunku żyją w tym samym wieku. Oznacza to, że wszyscy przodkowie zmarłego przodka również muszą być martwi - podobnie wszyscy potomkowie żywego przodka muszą żyć.
źródło
Robię wywiady do pracy i również źle wyglądam na twoją odpowiedź (przepraszam, ale jestem bardzo szczery). Brzmi tak, jakbyś przeczytał o różnicy i poprawił odpowiedź, ale być może nigdy nie używałeś jej w praktyce.
Dobre wyjaśnienie, dlaczego warto użyć każdego z nich, może być znacznie lepsze niż precyzyjne wyjaśnienie różnicy. Pracodawcy ultimatley chcą, aby programiści robili rzeczy, których nie znają, co może być trudne do wykazania w wywiadzie. Odpowiedź, którą podasz, byłaby dobra, gdybyś ubiegał się o pracę w oparciu o dokumentację techniczną lub dokumentację, ale nie byłby programistą.
Powodzenia w wywiadach w przyszłości.
Moja odpowiedź na to pytanie dotyczy raczej techniki wywiadu, a nie dostarczonego materiału technicznego. Może warto przeczytać o tym. https://workplace.stackexchange.com/ może być doskonałym miejscem na tego typu rzeczy.
źródło
Wybieramy Interfejs w Javie, aby uniknąć Diamentowego Problemu podczas wielokrotnego dziedziczenia .
Jeśli chcesz, aby wszystkie metody zostały zaimplementowane przez klienta, wybierz interfejs. Oznacza to, że projektujesz całą aplikację w sposób abstrakcyjny.
Wybieracie klasę abstrakcyjną, jeśli już wiecie, co jest wspólne. Na przykład Weź klasę abstrakcyjną
Car
. Na wyższym poziomie wdrażasz typowe metody samochodowe, takie jakcalculateRPM()
. Jest to powszechna metoda i pozwalasz klientowi wdrożyć własne zachowanie, takie jakcalculateMaxSpeed()
itp. Prawdopodobnie wyjaśniłbyś, podając kilka przykładów w czasie rzeczywistym, które napotkałeś w codziennej pracy.źródło
Interfejs jest czysto abstrakcyjny. nie mamy żadnego kodu implementacyjnego w interfejsie.
Klasa abstrakcyjna zawiera obie metody i jej implementację.
kliknij tutaj, aby obejrzeć samouczek dotyczący interfejsów i klas abstrakcyjnych
źródło
Główną różnicą, którą zaobserwowałem, było to, że klasa abstrakcyjna zapewnia nam pewne typowe zachowania już zaimplementowane, a podklasy muszą jedynie zaimplementować odpowiadającą im funkcjonalność. gdzie jak dla interfejsu określa tylko zadania, które należy wykonać, a interfejs nie podaje żadnych implementacji. Mogę powiedzieć, że określa umowę między sobą a zaimplementowanymi klasami.
źródło
Nawet ja spotkałem się z tym samym pytaniem w wielu wywiadach i wierzcie mi, że przekonywanie rozmówcy sprawia, że wasz czas jest nieszczęśliwy. Jeśli wpisuję wszystkie odpowiedzi z góry, muszę dodać jeszcze jeden kluczowy punkt, aby był bardziej przekonujący i wykorzystywał OO w najlepszym wydaniu
W przypadku, gdy nie planujesz żadnych modyfikacji w regułach , dla podklasy, która ma być przestrzegana, w długiej przyszłości wybierz interfejs, ponieważ nie będziesz mógł go modyfikować, a jeśli to zrobisz, musisz przejść do zmiany we wszystkich pozostałych podklasach, a jeśli uważasz, że chcesz ponownie użyć tej funkcji, ustawić pewne reguły, a także umożliwić jej modyfikację , przejdź do klasy Abstract.
Pomyśl w ten sposób, że korzystałeś z usługi eksploatacyjnej lub podałeś jakiś kod światu i masz szansę zmodyfikować coś, przypuśćmy kontrolę bezpieczeństwa. A jeśli jestem konsumentem kodu i któregoś ranka po aktualizacji, I znajdź wszystkie znaki odczytu w moim Eclipse, cała aplikacja jest wyłączona. Aby zapobiec takim koszmarom, użyj opcji Streszczenie zamiast interfejsów
Myślę, że to może do pewnego stopnia przekonać Ankietera ... Szczęśliwe wywiady z wyprzedzeniem.
źródło
Kiedy próbuję podzielić zachowanie między 2 blisko spokrewnionymi klasami, tworzę abstrakcyjną klasę, która zawiera wspólne zachowanie i służy jako rodzic dla obu klas.
Kiedy próbuję zdefiniować typ, listę metod, które użytkownik mojego obiektu może rzetelnie wywołać, a następnie tworzę interfejs.
Na przykład nigdy nie stworzyłbym klasy abstrakcyjnej z 1 konkretną podklasą, ponieważ klasy abstrakcyjne dotyczą zachowania współużytkowania. Ale równie dobrze mogę stworzyć interfejs z tylko jedną implementacją. Użytkownik mojego kodu nie będzie wiedział, że istnieje tylko jedna implementacja. Rzeczywiście, w przyszłej wersji może być kilka implementacji, z których wszystkie są podklasami jakiejś nowej klasy abstrakcyjnej, która nawet nie istniała, kiedy tworzyłem interfejs.
To też może wydawać się trochę zbyt książkowe (choć nigdy nie widziałem, żeby było to tak nigdzie przypominam). Gdyby osoba przeprowadzająca wywiad (lub OP) naprawdę chciała więcej moich osobistych doświadczeń w tym zakresie, byłbym gotowy z anegdotami dotyczącymi interfejsu, który ewoluował z konieczności i odwrotnie.
Jeszcze jedna rzecz. Java 8 pozwala teraz umieścić domyślny kod w interfejsie, dodatkowo zacierając linię między interfejsami a klasami abstrakcyjnymi. Ale z tego, co widziałem, ta funkcja jest nadużywana nawet przez twórców podstawowych bibliotek Java. Ta funkcja została dodana, i słusznie, aby umożliwić rozszerzenie interfejsu bez tworzenia binarnej niezgodności. Ale jeśli tworzysz nowy typ, definiując interfejs, interfejs powinien być TYLKO interfejsem. Jeśli chcesz również podać wspólny kod, to z całą pewnością stwórz klasę pomocniczą (abstrakcyjną lub konkretną). Nie zaśmiecaj interfejsu od samego początku funkcjonalnością, którą możesz zmienić.
źródło
Podstawowa różnica między interfejsem a klasą abstrakcyjną polega na tym, że interfejs obsługuje wielokrotne dziedziczenie, ale klasa abstrakcyjna nie.
W klasie abstrakcyjnej można również udostępnić wszystkie metody abstrakcyjne, takie jak interfejs.
dlaczego wymagana jest klasa abstrakcyjna?
W niektórych scenariuszach podczas przetwarzania żądania użytkownika klasa abstrakcyjna nie wie, jaki jest zamiar użytkownika. W tym scenariuszu zdefiniujemy jedną metodę abstrakcyjną w klasie i poprosimy użytkownika, który rozszerza tę klasę, o podanie swojej intencji w metodzie abstrakcyjnej. W tym przypadku klasy abstrakcyjne są bardzo przydatne
Dlaczego interfejs jest wymagany?
Powiedzmy, że mam pracę, której nie mam doświadczenia w tej dziedzinie. Na przykład, jeśli chcesz zbudować budynek lub tamę, to co zrobisz w tym scenariuszu?
Tutaj nie zawracam sobie głowy logiką ich budowy. Ostateczny obiekt spełnił moje wymagania lub nie, to tylko mój kluczowy punkt.
Tutaj twoje wymagania zwane interfejsem, a konstruktory są nazywane implementatorem.
źródło
W kilku słowach odpowiedziałbym w ten sposób:
Klasy abstrakcyjne mogą być traktowane jako coś pomiędzy tymi dwoma przypadkami (wprowadza pewien stan, ale także zobowiązuje do zdefiniowania zachowania), klasa w pełni abstrakcyjna jest interfejsem (jest to dalszy rozwój klas składających się z metod wirtualnych tylko w C ++, ponieważ o ile wiem o jego składni).
Oczywiście, począwszy od Javy 8, sprawy nieco się zmieniły, ale pomysł jest taki sam.
Myślę, że to wystarcza do typowego wywiadu w języku Java, jeśli nie przeprowadzamy wywiadu z zespołem zajmującym się kompilacją.
źródło
Z tego, co rozumiem, interfejs, który składa się z końcowych zmiennych i metod bez implementacji, jest implementowany przez klasę w celu uzyskania grupy metod lub metod, które są ze sobą powiązane. Z drugiej strony klasa abstrakcyjna, która może zawierać nie-końcowe zmienne i metody z implementacjami, jest zwykle używana jako przewodnik lub nadklasa, z której dziedziczą wszystkie klasy pokrewne lub podobne. Innymi słowy, klasa abstrakcyjna zawiera wszystkie metody / zmienne, które są wspólne dla wszystkich jej podklas.
źródło
W klasie abstrakcyjnej możesz napisać domyślną implementację metod! Ale w interfejsie nie możesz. Zasadniczo w interfejsie istnieją metody czysto wirtualne, które muszą zostać zaimplementowane przez klasę implementującą interfejs.
źródło
Tak, twoje odpowiedzi były technicznie poprawne, ale to, że popełniłeś błąd, nie pokazywało im, że rozumiesz wady i zalety wyboru jednej z drugiej. Dodatkowo, prawdopodobnie martwili się / dziwili się na temat zgodności ich bazy kodu z aktualizacjami w przyszłości. Ten rodzaj odpowiedzi mógł pomóc (oprócz tego, co powiedziałeś):
Powodzenia w kolejnym wywiadzie!
źródło
Spróbuję odpowiedzieć, używając praktycznego scenariusza, aby pokazać różnicę między nimi.
Interfejsy są dostarczane z zerowym obciążeniem, tzn. Nie trzeba utrzymywać żadnego stanu, a zatem są lepszym wyborem, aby powiązać kontrakt (zdolność) z klasą.
Powiedzmy na przykład, że mam klasę Task, która wykonuje jakąś akcję, teraz aby wykonać zadanie w osobnym wątku, tak naprawdę nie muszę rozszerzać klasy Thread, raczej lepszym wyborem jest sprawienie, aby Task implementował interfejs Runnable (tj. Zaimplementował metodę run () ), a następnie przekaż obiekt tej klasy Task do instancji Thread i wywołaj jej metodę start ().
Technicznie było to możliwe, ale pod względem projektowym byłby to zły wybór:
Innymi słowy, klasa Task wymagała zdolności do uruchomienia w wątku, który osiągnęła dzięki zaimplementowaniu wersetów interfejsu Runnable rozszerzających klasę Thread, które uczyniłyby z niej wątek.
Uwaga: głupi przykład poniżej, staraj się nie oceniać :-P
Teraz masz wybór, aby być Bogiem, ale możesz wybrać tylko Przebaczenie (tj. Nie Godlike) i wykonać:
Lub możesz wybrać bycie Bogiem i wykonać:
PS z interfejsem java 8 może również posiadać metody statyczne i domyślne (implementacja nadpisywalna), dzięki czemu różnica między czarno-białym interfejsem a klasą abstrakcyjną jest jeszcze bardziej zawężona.
źródło
Wydaje się, że już prawie wszystko zostało tutaj omówione. Dodanie jeszcze jednego punktu na temat praktycznej implementacji
abstract
klasy:źródło
hmm, teraz ludzie są głodni praktycznego podejścia, masz rację, ale większość ankieterów wygląda zgodnie z ich obecnymi wymaganiami i chce praktycznego podejścia.
po zakończeniu odpowiedzi powinieneś skoczyć na przykład:
Abstrakcyjny:
na przykład mamy funkcję wynagrodzenia, która ma pewien parametr wspólny dla wszystkich pracowników. wtedy możemy mieć klasę abstrakcyjną o nazwie CTC z częściowo zdefiniowanym ciałem metody, która zostanie rozszerzona przez wszystkich typów pracowników i zostanie ponownie zaprojektowana zgodnie z ich dodatkowymi cechami. Dla wspólnej funkcjonalności.
Berło
Interfejs w java pozwala mieć funkcjonalność interfejsu bez rozszerzania tej i musisz być przejrzysty dzięki implementacji sygnatury funkcjonalności, którą chcesz wprowadzić w swojej aplikacji. zmusi cię do posiadania definicji. Dla różnych funkcji.
możesz mieć taką wymuszoną aktywność również z klasą abstrakcyjną przez zdefiniowane metody jako abstrakcyjne, teraz klasa, która rozszerza klasę abstrakcyjną, reminuje abstrakcyjną, dopóki nie nadpisze tej funkcji abstrakcyjnej.
źródło
Z tego, co rozumiem i jak podchodzę,
Interfejs jest jak specyfikacja / umowa, każda klasa implementująca klasę interfejsu musi implementować wszystkie metody zdefiniowane w klasie abstrakcyjnej (z wyjątkiem metod domyślnych (wprowadzonych w Javie 8))
Podczas gdy definiuję streszczenie klasy, gdy wiem, że implementacja jest wymagana dla niektórych metod klasy i niektórych metod, nadal nie wiem, co będzie implementacją (możemy znać sygnaturę funkcji, ale nie implementację). Robię to, aby później, w części programistycznej, kiedy wiem, jak te metody mają zostać zaimplementowane, mogę po prostu rozszerzyć tę abstrakcyjną klasę i zaimplementować te metody.
Uwaga: Nie można mieć treści funkcji w metodach interfejsu, chyba że metoda jest statyczna lub domyślna.
źródło
Uważam, że to, o co pytał ankieter, było prawdopodobnie różnicą między interfejsem a implementacją.
Interfejs - nie interfejs Java, ale „interfejs” w bardziej ogólnym ujęciu - z modułem kodu jest w zasadzie umową zawartą z kodem klienta korzystającym z interfejsu.
Implementacja modułu kodu to wewnętrzny kod, który powoduje, że moduł działa. Często możesz wdrożyć określony interfejs na więcej niż jeden inny sposób, a nawet zmienić implementację bez kodu klienta, nawet nie wiedząc o zmianie.
Interfejs Java powinien być używany wyłącznie jako interfejs w powyższym sensie ogólnym, aby zdefiniować zachowanie klasy na korzyść kodu klienta korzystającego z klasy, bez określania jakiejkolwiek implementacji. Interfejs zawiera zatem sygnatury metod - nazwy, typy zwracane i listy argumentów - dla metod, które mają być wywoływane przez kod klienta, i zasadniczo powinien mieć mnóstwo Javadoc dla każdej metody opisującej działanie tej metody. Najbardziej przekonującym powodem korzystania z interfejsu jest planowanie wielu różnych implementacji interfejsu, być może wybór implementacji zależy od konfiguracji wdrożenia.
Natomiast klasa abstrakcyjna Java zapewnia częściową implementację klasy, a nie jej głównym celem jest określenie interfejsu. Powinno być używane, gdy wiele klas współdzieli kod, ale gdy oczekuje się, że podklasy zapewnią także część implementacji. Dzięki temu wspólny kod może pojawiać się tylko w jednym miejscu - klasie abstrakcyjnej - jednocześnie wyjaśniając, że części implementacji nie występują w klasie abstrakcyjnej i oczekuje się, że zostaną dostarczone przez podklasy.
źródło
twoja odpowiedź jest prawidłowa, ale osoba przeprowadzająca wywiad wymaga rozróżnienia według perspektywy inżynierii oprogramowania, a nie szczegółów Java.
Proste słowa:
Interfejs jest jak interfejs sklepu, wszystko, co jest na nim pokazane, powinno znajdować się w sklepie, więc każda metoda w interfejsie musi być tam zaimplementowana w konkretnej klasie. A co, jeśli niektóre klasy mają pewne dokładne metody i różnią się w innych. Załóżmy, że interfejs dotyczy sklepu, który zawiera dwie rzeczy i załóżmy, że oba sklepy zawierają sprzęt sportowy, ale jeden ma dodatkowe ubrania, a drugi dodatkowe buty. Więc tworzysz abstrakcyjną klasę dla sportu, która implementuje metodę Sports i pozostawia inną metodę niezaimplementowaną. Klasa abstrakcyjna tutaj oznacza, że ten sklep sam nie istnieje, ale jest bazą dla innych klas / sklepów. W ten sposób organizujesz kod, unikając błędów przy replikacji kodu, ujednolicając kod i zapewniając możliwość ponownego użycia przez inną klasę.
źródło