Dědičnost
V předchozích lekcích jste viděli několikrát zmíněnou dědičnost. V jazyce Java lze třídy odvodit z jiných tříd, čímž zdědí pole a metody z těchto tříd.
Kromě
Object
, která nemá žádnou nadtřídu, má každá třída jednu a pouze jedna přímá nadtřída (jednoduchá dědičnost). Při absenci jakékoli jiné explicitní nadtřídy je každá třída implicitně podtřídou Object
. Třídy lze odvodit od tříd odvozených od tříd odvozených od tříd a atd. a nakonec odvozeno od nejvyšší třídy
Object
. Říká se, že taková třída pochází ze všech tříd v dědickém řetězci sahajících až k Object
. Myšlenka dědičnosti je jednoduchá, ale výkonná: Když chcete vytvořit novou třídu a již existuje třída, která obsahuje část požadovaného kódu, můžete svou novou třídu odvodit z existující třídy . Přitom můžete znovu použít pole a metody existující třídy, aniž byste je museli sami psát (a ladit!).
Podtřída zdědí všechny členy (pole, metody a vnořené třídy) z jeho nadtřída. Konstruktory nejsou členy, takže je nezdědí podtřídy, ale konstruktor nadtřídy lze vyvolat z podtřídy.
Hierarchie tříd platformy Java
The Object
definovaná v balíčku java.lang
definuje a implementuje chování společné pro všechny třídy – včetně těch, které píšete. Na platformě Java je mnoho tříd odvozeno přímo od Object
, další třídy jsou odvozeny od některých z těchto tříd atd. A vytvářejí hierarchii tříd.
Všechny třídy v platformě Java jsou potomky objektu
V horní části hierarchie je Object
nejobecnější ze všech tříd. Třídy ve spodní části hierarchie poskytují specializovanější chování.
Příklad dědičnosti
Zde je ukázkový kód pro možnou implementaci Bicycle
třída, která byla představena v lekci Třídy a objekty:
Deklarace třídy pro třídu MountainBike
, která je podtřídou Bicycle
může vypadat takto:
public class MountainBike extends Bicycle { // the MountainBike subclass adds one field public int seatHeight; // the MountainBike subclass has one constructor public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) { super(startCadence, startSpeed, startGear); seatHeight = startHeight; } // the MountainBike subclass adds one method public void setHeight(int newValue) { seatHeight = newValue; } }
MountainBike
dědí všechna pole a metody Bicycle
a přidá pole seatHeight
a způsob jeho nastavení. Kromě konstruktoru je to, jako byste novou třídu MountainBike
napsali úplně od začátku se čtyřmi poli a pěti metodami. Nemuseli jste však dělat veškerou práci. To by bylo obzvláště cenné, kdyby metody ve třídě Bicycle
byly složité a jejich ladění trvalo značně dlouho.
Co můžete dělat v podtřídě
Podtřída zdědí všechny veřejné a chráněné členy svého rodiče, bez ohledu na to, v jakém balíčku je podtřída. Pokud je podtřída ve stejném balíčku jako její nadřazený, zdědí také členy soukromého balíčku nadřazeného. Zděděné členy můžete použít tak, jak jsou, nahradit je, skrýt nebo doplnit novými členy:
- Zděděná pole mohou použít přímo, stejně jako kterákoli jiná pole.
- Pole v podtřídě můžete deklarovat se stejným názvem jako v nadtřídě, a tím ji skryjete (nedoporučuje se).
- V podtřídě můžete deklarovat nová pole, která nejsou v nadtřídě.
- Zděděné metody lze použít přímo tak, jak jsou.
- Do metody můžete napsat novou metodu instance podtřída to má stejný podpis jako ten v nadtřídě, čímž ji přepíše.
- Do podtřídy můžete napsat novou statickou metodu, která má stejný podpis jako ten v nadtřídě, a tím ji skryjete.
- V podtřídě můžete deklarovat nové metody, které nejsou v nadtřídě.
- Můžete napsat konstruktor podtřídy, který vyvolá konstruktor nadtřídy, buď implicitně, nebo pomocí klíčového slova
super
.
Následující části této lekce se budou těmito tématy rozšiřovat.
Soukromí členové v nadtřídě
Podtřída nezdědí private
členy své nadřazené třídy. Pokud však nadtřída má veřejné nebo chráněné metody pro přístup ke svým soukromým polím, lze je také použít v podtřídě.
Vnořená třída má přístup ke všem soukromým členům její obklopující třídy – k polím i metodám. Proto má veřejná nebo chráněná vnořená třída zděděná podtřídou nepřímý přístup ke všem soukromým členům nadtřídy.
Casting Objects
Viděli jsme, že objekt je datový typ třídy, ze které byl vytvořen. Například pokud napíšeme
public MountainBike myBike = new MountainBike();
, pak myBike
je typu MountainBike
.
MountainBike
pochází z Bicycle
a Object
. Proto MountainBike
je Bicycle
a je také Object
a může být používá se všude, kde jsou požadovány objekty Bicycle
nebo Object
.
Opak nemusí nutně platit: a Bicycle
může být MountainBike
, ale není to nutně. Podobně může být Object
Bicycle
nebo MountainBike
, ale není to nutně.
Casting ukazuje použití objektu jednoho zadejte místo jiného typu mezi objekty povolené dědičností a implementacemi. Pokud například napíšeme
Object obj = new MountainBike();
, pak obj
je Object
a MountainBike
(dokud obj
nebude přiřazen jiný objekt, který není MountainBike
). Tomu se říká implicitní casting.
Pokud naopak napíšeme
MountainBike myBike = obj;
udělali bychom získejte chybu v době kompilace, protože obj
není kompilátoru známo, že je MountainBike
. Můžeme však kompilátoru říci, že slibujeme, že MountainBike
přiřadíme obj
explicitním castingem:
MountainBike myBike = (MountainBike)obj;
Toto obsazení vloží běhovou kontrolu, zda je obj
přiřazeno MountainBike
, aby kompilátor mohl bezpečně předpokládat, že obj
je MountainBike
. Pokud obj
není MountainBike
za běhu, bude vyvolána výjimka.
instanceof
. To vás může zachránit před runtime chybou v důsledku nesprávného obsazení. Například:
if (obj instanceof MountainBike) { MountainBike myBike = (MountainBike)obj;}
Zde operátor instanceof
ověří, že obj
odkazuje na MountainBike
, abychom mohli provést obsazení s vědomím, že nebude vyvolána žádná runtime výjimka.