Programování

Dědičnost v Javě, část 1: Klíčové slovo extends

Java podporuje opětovné použití tříd prostřednictvím dědičnosti a složení. Tento dvoudílný výukový program vás naučí, jak používat dědičnost ve vašich programech Java. V části 1 se naučíte používat rozšiřuje klíčové slovo pro odvození podřízené třídy z nadřazené třídy, vyvolání konstruktorů a metod nadřazené třídy a přepsání metod. V části 2 budete cestovat java.lang.Object, což je nadtřída Javy, ze které dědí všechny ostatní třídy.

Chcete-li dokončit své učení o dědičnosti, nezapomeňte se podívat na můj tip Java vysvětlující, kdy použít složení vs dědičnost. Dozvíte se, proč je kompozice důležitým doplňkem dědičnosti a jak ji používat k ochraně před problémy se zapouzdřením ve vašich programech Java.

stáhnout Získat kód Stáhněte si zdrojový kód například pro aplikace v tomto výukovém programu. Vytvořil Jeff Friesen pro JavaWorld.

Dědičnost Java: Dva příklady

Dědictví je programovací konstrukt, který vývojáři softwaru používají k vytvoření je-vztahy mezi kategoriemi. Dědičnost nám umožňuje odvodit konkrétnější kategorie od obecnějších. Přesnější kategorie je druh obecnější kategorie. Například běžný účet je druh účtu, na kterém můžete provádět vklady a výběry. Podobně je nákladní automobil druh vozidla používaného k přepravě velkých předmětů.

Dědičnost může sestoupit přes několik úrovní, což povede ke stále konkrétnějším kategoriím. Jako příklad ukazuje obrázek 1 osobní a nákladní vozidlo zděděné od vozidla; kombi dědí z automobilu; a popelářský vůz zděděný od kamionu. Šipky směřují od konkrétnějších „podřízených“ kategorií (nižší dolů) k méně konkrétním „nadřazeným“ kategoriím (vyšší nahoru).

Jeff Friesen

Tento příklad ilustruje jedno dědictví ve kterém podřízená kategorie dědí stav a chování z jedné bezprostřední nadřazené kategorie. V porovnání, vícenásobné dědictví umožňuje podřízené kategorii zdědit stav a chování ze dvou nebo více bezprostředních nadřazených kategorií. Hierarchie na obrázku 2 ilustruje vícenásobnou dědičnost.

Jeff Friesen

Kategorie jsou popsány třídami. Java podporuje jediné dědictví prostřednictvím rozšíření třídy, ve kterém jedna třída přímo dědí přístupná pole a metody z jiné třídy rozšířením této třídy. Java však nepodporuje vícenásobné dědění prostřednictvím rozšíření třídy.

Při prohlížení hierarchie dědičnosti můžete snadno zjistit vícenásobnou dědičnost podle přítomnosti diamantového vzoru. Obrázek 2 ukazuje tento vzorec v kontextu vozidla, pozemního vozidla, vodního vozidla a vznášedla.

Klíčové slovo extends

Java podporuje rozšíření třídy přes rozšiřuje klíčové slovo. Pokud jsou přítomny, rozšiřuje určuje vztah rodič-dítě mezi dvěma třídami. Níže používám rozšiřuje navázat vztah mezi třídami Vozidlo a Autoa poté mezi Účet a Spořící účet:

Výpis 1. The rozšiřuje klíčové slovo určuje vztah rodič-dítě

třída Vehicle {// deklarace členů} třída Car rozšiřuje Vehicle {// dědí přístupné členy z Vehicle // poskytuje vlastní deklarace členů} třída Účet {// deklarace členů} třída SavingsAccount rozšiřuje Účet {// dědí přístupné členy z Účtu // poskytovat vlastní prohlášení člena}

The rozšiřuje klíčové slovo je zadáno za názvem třídy a před názvem jiné třídy. Název třídy dříve rozšiřuje identifikuje dítě a název třídy za rozšiřuje identifikuje rodiče. Je nemožné zadat více názvů tříd po rozšiřuje protože Java nepodporuje vícenásobné dědictví založené na třídách.

Tyto příklady kodifikují vztahy typu is-a: Autoje specializované Vozidlo a Spořící účetje specializované Účet. Vozidlo a Účet jsou známé jako základní třídy, mateřské třídynebo nadtřídy. Auto a Spořící účet jsou známé jako odvozené třídy, dětské třídynebo podtřídy.

Závěrečné třídy

Můžete deklarovat třídu, která by neměla být rozšířena; například z bezpečnostních důvodů. V Javě používáme finále klíčové slovo, aby se zabránilo rozšíření některých tříd. Jednoduše zadejte předponu záhlaví třídy pomocí finále, jako v poslední třída Heslo. Vzhledem k tomuto prohlášení kompilátor nahlásí chybu, pokud se někdo pokusí o prodloužení Heslo.

Podřízené třídy dědí přístupná pole a metody od svých nadřazených tříd a dalších předků. Nikdy však nezdědí konstruktory. Místo toho podřízené třídy deklarují své vlastní konstruktory. Dále mohou deklarovat svá vlastní pole a metody, jak je odlišit od svých rodičů. Zvažte výpis 2.

Výpis 2. An Účet mateřská třída

třída Účet {soukromý název řetězce; soukromá dlouhá částka; Účet (název řetězce, dlouhá částka) {this.name = name; setAmount (částka); } neplatný vklad (dlouhá částka) {this.amount + = částka; } Řetězec getName () {návratové jméno; } long getAmount () {vrácená částka; } void setAmount (dlouhé množství) {this.amount = amount; }}

Výpis 2 popisuje obecnou třídu bankovního účtu, která má název a počáteční částku a které jsou obě nastaveny v konstruktoru. Umožňuje také uživatelům provádět vklady. (Výběry můžete provádět vkladem záporných částek peněz, ale tuto možnost budeme ignorovat.) Pamatujte, že při vytváření účtu musí být nastaven název účtu.

Reprezentující hodnoty měny

počet haléřů. Můžete raději použít a dvojnásobek nebo a plovák ukládat peněžní hodnoty, ale to může vést k nepřesnostem. Pro lepší řešení zvažte BigDecimal, která je součástí standardní knihovny tříd Java.

Výpis 3 představuje a Spořící účet dětská třída, která rozšiřuje své Účet mateřská třída.

Výpis 3. A Spořící účet dětská třída rozšiřuje své Účet mateřská třída

třída SavingsAccount rozšiřuje Účet {SavingsAccount (dlouhá částka) {super ("úspory", částka); }}

The Spořící účet třída je triviální, protože nepotřebuje deklarovat další pole nebo metody. Deklaruje však konstruktor, který inicializuje pole ve svém Účet nadtřída. Inicializace se stane, když ÚčetKonstruktor se volá přes Java super klíčové slovo následované seznamem argumentů v závorkách.

Kdy a kde volat super ()

Stejně jako tento() musí být prvním prvkem v konstruktoru, který volá jiný konstruktor ve stejné třídě, super () must be the first element in a constructor that calls a constructor in its superclass. Pokud toto pravidlo porušíte, kompilátor ohlásí chybu. Kompilátor také nahlásí chybu, pokud zjistí a super () zavolat metodu; jen zavolat super () v konstruktoru.

Výpis 4 se dále rozšiřuje Účet s Kontrolní účet třída.

Výpis 4. A Kontrolní účet dětská třída rozšiřuje své Účet mateřská třída

třída CheckingAccount rozšiřuje účet {CheckingAccount (dlouhá částka) {super ("kontrola", částka); } zrušit výběr (dlouhá částka) {setAmount (getAmount () - částka); }}

Kontrolní účet je o něco podstatnější než Spořící účet protože deklaruje a ustoupit() metoda. Všimněte si volání této metody na setAmount () a getAmount (), který Kontrolní účet dědí z Účet. Nemůžete přímo přistupovat k množství pole v Účet protože toto pole je deklarováno soukromé (viz Výpis 2).

super () a konstruktor bez argumentů

Li super () není zadán v konstruktoru podtřídy, a pokud nadtřída nedeklaruje a žádný argument konstruktor, pak kompilátor ohlásí chybu. Je to proto, že konstruktor podtřídy musí volat a žádný argument konstruktor nadtřídy, když super () není přítomen.

Příklad hierarchie tříd

Vytvořil jsem Demo účtu třída aplikace, která vám umožní vyzkoušet Účet hierarchie tříd. Nejprve se podívejte na Demo účtuzdrojový kód.

Výpis 5. Demo účtu ukazuje hierarchii tříd účtů

třída AccountDemo {public static void main (String [] args) {SavingsAccount sa = nový SavingsAccount (10 000); System.out.println ("název účtu:" + sa.getName ()); System.out.println ("počáteční částka:" + sa.getAmount ()); sa.deposit (5000); System.out.println ("nová částka po vkladu:" + sa.getAmount ()); CheckingAccount ca = nový CheckingAccount (20000); System.out.println ("název účtu:" + ca.getName ()); System.out.println ("počáteční částka:" + ca.getAmount ()); ca. vklad (6000); System.out.println ("nová částka po vkladu:" + ca.getAmount ()); ca. výběr (3000); System.out.println ("nová částka po výběru:" + ca.getAmount ()); }}

The hlavní() metoda v seznamu 5 nejprve ukazuje Spořící účet, pak Kontrolní účet. Za předpokladu Účet.java, SavingsAccount.java, CheckingAccount.java, a AccountDemo.java zdrojové soubory jsou ve stejném adresáři, proveďte některý z následujících příkazů a zkompilujte všechny tyto zdrojové soubory:

javac AccountDemo.java javac * .java

Spusťte aplikaci pomocí následujícího příkazu:

java AccountDemo

Měli byste dodržovat následující výstup:

název účtu: počáteční částka spoření: 10000 nová částka po vkladu: 15000 název účtu: kontrola počáteční částky: 20000 nová částka po vkladu: 26000 nová částka po výběru: 23000

Přepsání metody (a přetížení metody)

Podtřída může přepsat (nahradit) zděděnou metodu, aby se místo toho volala verze metody podtřídy. Přepisující metoda musí zadat stejný název, seznam parametrů a návratový typ jako přepsaná metoda. K prokázání jsem prohlásil tisk() metoda v Vozidlo třída níže.

Výpis 6. Deklarace a tisk() způsob, který má být přepsán

třída Vehicle {private String make; soukromý řetězcový model; soukromý int rok; Vehicle (String make, String model, int year) {this.make = make; this.model = model; this.year = rok; } String getMake () {return make; } String getModel () {návratový model; } int getYear () {rok návratu; } void print () {System.out.println ("Make:" + make + ", Model:" + model + ", Year:" + year); }}

Dále přepíšu tisk() v Kamion třída.

Výpis 7. Přepsání tisk() v Kamion podtřída

třída Truck rozšiřuje vozidlo {soukromá dvojnásobná prostornost; Truck (značka, řetězec, model, rok výroby, dvojitá tonáž) {super (značka, model, rok); this.tonnage = prostornost; } dvojnásobek getTonnage () {návratnost; } void print () {super.print (); System.out.println ("Tonáž:" + tonáž); }}

Kamionje tisk() metoda má stejný název, návratový typ a seznam parametrů jako Vozidloje tisk() metoda. Všimněte si také, že Kamionje tisk() metoda první volání Vozidloje tisk() metoda předponou super. na název metody. Často je dobré nejprve provést logiku nadtřídy a poté provést logiku podtřídy.

Volání metod nadtřídy z metod podtříd

Chcete-li volat metodu nadtřídy z převažující metody podtřídy, zadejte před název metody vyhrazené slovo super a operátor přístupu členů. Jinak nakonec rekurzivně zavoláte přepisující metodu podtřídy. V některých případech bude podtřída maskovatsoukromé pole nadtřídy deklarováním stejnojmenných polí. Můžeš použít super a operátor přístupu členů k přístupu ksoukromé pole nadtřídy.

Pro dokončení tohoto příkladu jsem výňatek a VehicleDemo třídy hlavní() metoda:

Truck truck = new Truck ("Ford", "F150", 2008, 0.5); System.out.println ("Make =" + truck.getMake ()); System.out.println ("Model =" + truck.getModel ()); System.out.println ("Year =" + truck.getYear ()); System.out.println ("Tonnage =" + truck.getTonnage ()); truck.print ();

Poslední řádek, truck.print ();, volá kamionje tisk() metoda. Tato metoda nejprve volá Vozidloje tisk() vydat značku, model a rok nákladního vozidla; poté vydá tonáž nákladního vozidla. Tato část výstupu je uvedena níže:

Značka: Ford, Model: F150, rok: 2008 Tonáž: 0,5

K zablokování přepsání metody použijte final

Občas možná budete muset deklarovat metodu, která by neměla být přepsána, z bezpečnostních nebo jiných důvodů. Můžete použít finále klíčové slovo pro tento účel. Chcete-li zabránit přepsání, jednoduše předponu záhlaví metody finále, jako v final String getMake (). Kompilátor poté nahlásí chybu, pokud se někdo pokusí přepsat tuto metodu v podtřídě.

Přetížení metody a přepsání

Předpokládejme, že jste nahradili tisk() metoda v seznamu 7 s níže uvedenou:

void print (vlastník řetězce) {System.out.print ("Vlastník:" + vlastník); super.print (); }

Upravený Kamion třída má nyní dva tisk() metody: předchozí explicitně deklarovaná metoda a metoda zděděná od Vozidlo. The neplatný tisk (majitel řetězce) metoda nepřepíše Vozidloje tisk() metoda. Místo toho přetížení to.

Můžete detekovat pokus o přetížení namísto přepsání metody v době kompilace předponou záhlaví metody podtřídy pomocí @ Přepis anotace:

@Override void print (vlastník řetězce) {System.out.print ("Vlastník:" + vlastník); super.print (); }

Upřesnění @ Přepis řekne kompilátoru, že daná metoda přepíše jinou metodu. Pokud se místo toho někdo pokusí přetížit metodu, kompilátor nahlásí chybu. Bez této anotace by kompilátor nehlásil chybu, protože přetížení metody je legální.

Kdy použít @Override

Rozvíjejte ve zvyku předepisovat přednostní metody @ Přepis. Tento zvyk vám pomůže odhalit chyby přetížení mnohem dříve.

$config[zx-auto] not found$config[zx-overlay] not found