Java: Abstraktní třídy, dědičnost a rozhraní
Z MiS
(Rozdíly mezi verzemi)
m (Doplněn odkaz na OOP) |
(Doplnění abstraktních tříd.) |
||
(Není zobrazeno 7 mezilehlých verzí od 1 uživatele.) | |||
Řádka 2: | Řádka 2: | ||
== Dědičnost == | == Dědičnost == | ||
− | |||
− | |||
− | == | + | * Klíčové slovo <code>extends</code>. |
− | * Klíčové slovo <code> | + | * Třída může mít pouze jednu rodičovskou třídu! |
− | * | + | * Potomek „dědí“ všechny vlastnosti předka. |
+ | * Potomek může kdykoli nahradit předka. Takže pokud máme proměnnou datového typu <code>Predek</code>, můžeme do ní uložit instanci třídy <code>Potomek</code>. Opačně to však nejde! | ||
+ | |||
+ | <div class="Priklad"> | ||
+ | public class Zivocich { | ||
+ | ... | ||
+ | } | ||
+ | public class Moucha '''extends''' Zivocich { | ||
+ | ... | ||
+ | } | ||
+ | </div> | ||
+ | |||
+ | |||
+ | === Překrývání metod === | ||
+ | * Potomek automaticky dědí všechny metody předka. Ty může používat jako vlastní metody. | ||
+ | * Potomek ale může metodu nadefinovat znovu, čímž ''překryje'' (''override'') původní chování metody předka. | ||
+ | |||
+ | <div class="Priklad"> | ||
+ | public class Zivocich { | ||
+ | ... | ||
+ | public void hybejSe() { this.move(5); } ''// Živočichové se pohybují vpravo.'' | ||
+ | ... | ||
+ | } | ||
+ | public class Moucha '''extends''' Zivocich { | ||
+ | ... | ||
+ | '''@Override''' | ||
+ | public void hybejSe() { | ||
+ | this.turn(30); | ||
+ | ''// Moucha se nebude pohybovat, jen se bude točit na místě!'' | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | </div> | ||
+ | |||
+ | === Odkaz na kód předka ('''super''') === | ||
+ | * Klíčové slovo <code>super</code>. | ||
+ | * Můžeme odkazovat: | ||
+ | ** Na konstruktor rodičovské třídy. | ||
+ | ** Na překrytou metodu předka. | ||
+ | |||
+ | ; Příklady | ||
+ | <div class="Priklad"> | ||
+ | public class Kamion { | ||
+ | int nosnost; | ||
+ | ... | ||
+ | public int getNosnost() { return this.nosnost; } | ||
+ | ... | ||
+ | } | ||
+ | public class KamionSPrivesem '''extends''' Kamion { | ||
+ | Prives prives; | ||
+ | ... | ||
+ | '''@Override''' | ||
+ | public int getNosnost() { | ||
+ | int nosnostKamionu = '''super'''.getNosnost(); ''// Volání překryté metody předka.'' | ||
+ | return nosnostKamionu+this.prives.getNosnost(); | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | </div> | ||
+ | <div class="Priklad"> | ||
+ | public class World { | ||
+ | ... | ||
+ | public World(int rozmerX, int rozmerY, int dilek) { ... } | ||
+ | ... | ||
+ | } | ||
+ | public class MyWorld '''extends''' World { | ||
+ | ... | ||
+ | public MyWorld() { | ||
+ | '''super'''(600, 400, 1); ''// Volání konstruktoru předka.'' | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | </div> | ||
== Rozhraní == | == Rozhraní == | ||
* Klíčové slovo <code>implements</code>. | * Klíčové slovo <code>implements</code>. | ||
* Může mít pouze hlavičky metod a veřejné konstantní atributy. | * Může mít pouze hlavičky metod a veřejné konstantní atributy. | ||
+ | * Implementací rozhraní garantujeme, že naše třída má určité metody. | ||
+ | * Zajišťujeme například to, aby objekty různých tříd šly umístit do kontejneru. | ||
+ | |||
+ | ; Deklarace rozhraní | ||
+ | |||
+ | <div class="Priklad"> | ||
+ | public interface Zasahnutelne { | ||
+ | public void zasah(Strela s)''';''' | ||
+ | ''// Neříkáme, '''jak''' se má objekt chovat.'' | ||
+ | ''// Jen vyžadujeme, že '''bude mít''' tuto metodu.'' | ||
+ | } | ||
+ | </div> | ||
+ | |||
+ | ; Třída, která implementuje rozhraní | ||
+ | <div class="Priklad"> | ||
+ | public class Prisera '''implements''' Zasahnutelne { | ||
+ | |||
+ | ''...'' | ||
+ | ''... Může mít různé metody a atributy, ale '''musí''' mít metodu zasah(...)!!!'' | ||
+ | ''...'' | ||
+ | |||
+ | public void zasah(Strela s) { | ||
+ | ''// Popíšeme, jak se má reagovat na zprávu/metodu zasah(...).'' | ||
+ | this.getWorld().removeObject(this); | ||
+ | } | ||
+ | </div> | ||
+ | |||
+ | == Abstraktní třída == | ||
+ | * Klíčové slovo <code>abstract</code>. | ||
+ | * Nelze vytvořit instance této třídy. | ||
+ | * Abstraktní třídu lze ale použít jako společného předka dalších metod, které už abstraktní být nemusí. | ||
+ | |||
+ | <div class="Priklad"> | ||
+ | public '''abstract''' class Predmet { | ||
+ | |||
+ | ''...'' | ||
+ | } | ||
+ | </div> | ||
+ | |||
+ | === Abstraktní metody === | ||
+ | * Abstraktní třída může mít abstraktní metody. | ||
+ | * U takových metod není uveden kód, který se bude provádět, pouze fakt, že metoda ve třídě musí být. | ||
+ | * Pokud potomci nebudou abstraktní, musí ke každé abstraktní metodě uvést konkrétní kód. | ||
+ | |||
+ | <div class="Priklad"> | ||
+ | public '''abstract''' class Predmet { | ||
+ | '''...''' | ||
+ | |||
+ | public '''abstract''' void pouzij()''';''' | ||
+ | |||
+ | '''...''' | ||
+ | } | ||
+ | |||
+ | public class Mic extends Predmet { | ||
+ | |||
+ | '''...''' | ||
+ | |||
+ | public void pouzij() | ||
+ | { | ||
+ | ''... // Co se má stát při volání metody pouzij().'' | ||
+ | } | ||
+ | |||
+ | '''...''' | ||
+ | } | ||
+ | </div> | ||
+ | |||
+ | |||
+ | == Srovnání konceptů == | ||
+ | ; Dědičnost | ||
+ | * Používáme tehdy, kdy potomek přebírá ''všechny'' vlastnosti předka (a případně přidává něco dalšího). | ||
+ | * Třída může mít ''jen jednoho'' předka. | ||
+ | ; Rozhraní | ||
+ | * Pouze předepisuje, jaké metody má třída mít. | ||
+ | * Nemůžeme z rozhraní převzít hotový kód. | ||
+ | * Nelze vytvořit instanci rozhraní (nelze volat <code>new Rozhrani()</code>). | ||
+ | * Můžeme implementovat kolik rozhraní chceme. | ||
+ | * Je užitečné, když každá třída má na společnou zprávu reagovat jinak. | ||
+ | ; Abstraktní třída | ||
+ | * Je jakýmsi mezistupněm, kdy přebíráme kód jen některých metod. | ||
+ | * Nelze vytvářet instance abstraktní třídy. | ||
+ | |||
+ | |||
+ | == Přetypování == | ||
+ | * Potomek může vždy zastoupit předka. Do proměnné datového typu předka tedy můžeme uložit instanci třídy potomka. | ||
+ | * Jsou ale situace, kdy potřebujeme k takto uloženému potomkovi znovu přistupovat jako k potomkovi včetně všech jeho metod a atributů. | ||
+ | * V tom případě musíme použít přetypování. | ||
+ | |||
+ | * Přetypování zapíšeme tak, že do závorky před výraz, který vrací takto uloženého potomka, zapíšeme název třídy předka. | ||
+ | |||
+ | ; Příklady přetypování | ||
+ | * Greenfoot a detekce kolize s konkrétním typem aktéra | ||
+ | |||
+ | Prekazka prekazka = '''(Prekazka)''' this.getOneIntersectingObject(Prekazka.class); | ||
+ | if (prekazka != null) { | ||
+ | int prekazka.getPruhlednost(); | ||
+ | ... | ||
+ | } | ||
+ | |||
+ | * GUI pomocí knihovny Swing — zjištění zdroje události | ||
+ | |||
+ | public void actionPerformed(ActionEvent event) { | ||
+ | Button zdroj = '''(Button)''' event.getSource(); | ||
+ | zdroj.getTitle(); | ||
+ | ... | ||
+ | } | ||
+ | |||
+ | <div class="Varovani">Pozor, musíme si být stoprocentně jisti, že objekt, který je uložen v proměnné datového typu <code>Predek</code> je ve skutečnosti opravdu datového typu <code>Potomek</code>. Pokud by nebyl, běh programu by skončil výjimečný stavem a aplikace by havarovala.</div> | ||
− | == | + | == Související stránky == |
* [[Objektově orientované programování]] | * [[Objektově orientované programování]] |
Aktuální verze z 24. 2. 2020, 14:31
Obsah |
Dědičnost
- Klíčové slovo
extends
. - Třída může mít pouze jednu rodičovskou třídu!
- Potomek „dědí“ všechny vlastnosti předka.
- Potomek může kdykoli nahradit předka. Takže pokud máme proměnnou datového typu
Predek
, můžeme do ní uložit instanci třídyPotomek
. Opačně to však nejde!
public class Zivocich { ... } public class Moucha extends Zivocich { ... }
Překrývání metod
- Potomek automaticky dědí všechny metody předka. Ty může používat jako vlastní metody.
- Potomek ale může metodu nadefinovat znovu, čímž překryje (override) původní chování metody předka.
public class Zivocich { ... public void hybejSe() { this.move(5); } // Živočichové se pohybují vpravo. ... } public class Moucha extends Zivocich { ... @Override public void hybejSe() { this.turn(30); // Moucha se nebude pohybovat, jen se bude točit na místě! } ... }
Odkaz na kód předka (super)
- Klíčové slovo
super
. - Můžeme odkazovat:
- Na konstruktor rodičovské třídy.
- Na překrytou metodu předka.
- Příklady
public class Kamion { int nosnost; ... public int getNosnost() { return this.nosnost; } ... } public class KamionSPrivesem extends Kamion { Prives prives; ... @Override public int getNosnost() { int nosnostKamionu = super.getNosnost(); // Volání překryté metody předka. return nosnostKamionu+this.prives.getNosnost(); } ... }
public class World { ... public World(int rozmerX, int rozmerY, int dilek) { ... } ... } public class MyWorld extends World { ... public MyWorld() { super(600, 400, 1); // Volání konstruktoru předka. } ... }
Rozhraní
- Klíčové slovo
implements
. - Může mít pouze hlavičky metod a veřejné konstantní atributy.
- Implementací rozhraní garantujeme, že naše třída má určité metody.
- Zajišťujeme například to, aby objekty různých tříd šly umístit do kontejneru.
- Deklarace rozhraní
public interface Zasahnutelne { public void zasah(Strela s); // Neříkáme, jak se má objekt chovat. // Jen vyžadujeme, že bude mít tuto metodu. }
- Třída, která implementuje rozhraní
public class Prisera implements Zasahnutelne { ... ... Může mít různé metody a atributy, ale musí mít metodu zasah(...)!!! ... public void zasah(Strela s) { // Popíšeme, jak se má reagovat na zprávu/metodu zasah(...). this.getWorld().removeObject(this); }
Abstraktní třída
- Klíčové slovo
abstract
. - Nelze vytvořit instance této třídy.
- Abstraktní třídu lze ale použít jako společného předka dalších metod, které už abstraktní být nemusí.
public abstract class Predmet { ... }
Abstraktní metody
- Abstraktní třída může mít abstraktní metody.
- U takových metod není uveden kód, který se bude provádět, pouze fakt, že metoda ve třídě musí být.
- Pokud potomci nebudou abstraktní, musí ke každé abstraktní metodě uvést konkrétní kód.
public abstract class Predmet { ...
public abstract void pouzij();
... }
public class Mic extends Predmet {
... public void pouzij() { ... // Co se má stát při volání metody pouzij(). }
... }
Srovnání konceptů
- Dědičnost
- Používáme tehdy, kdy potomek přebírá všechny vlastnosti předka (a případně přidává něco dalšího).
- Třída může mít jen jednoho předka.
- Rozhraní
- Pouze předepisuje, jaké metody má třída mít.
- Nemůžeme z rozhraní převzít hotový kód.
- Nelze vytvořit instanci rozhraní (nelze volat
new Rozhrani()
). - Můžeme implementovat kolik rozhraní chceme.
- Je užitečné, když každá třída má na společnou zprávu reagovat jinak.
- Abstraktní třída
- Je jakýmsi mezistupněm, kdy přebíráme kód jen některých metod.
- Nelze vytvářet instance abstraktní třídy.
Přetypování
- Potomek může vždy zastoupit předka. Do proměnné datového typu předka tedy můžeme uložit instanci třídy potomka.
- Jsou ale situace, kdy potřebujeme k takto uloženému potomkovi znovu přistupovat jako k potomkovi včetně všech jeho metod a atributů.
- V tom případě musíme použít přetypování.
- Přetypování zapíšeme tak, že do závorky před výraz, který vrací takto uloženého potomka, zapíšeme název třídy předka.
- Příklady přetypování
- Greenfoot a detekce kolize s konkrétním typem aktéra
Prekazka prekazka = (Prekazka) this.getOneIntersectingObject(Prekazka.class); if (prekazka != null) { int prekazka.getPruhlednost(); ... }
- GUI pomocí knihovny Swing — zjištění zdroje události
public void actionPerformed(ActionEvent event) { Button zdroj = (Button) event.getSource(); zdroj.getTitle(); ... }
Pozor, musíme si být stoprocentně jisti, že objekt, který je uložen v proměnné datového typu
Predek
je ve skutečnosti opravdu datového typu Potomek
. Pokud by nebyl, běh programu by skončil výjimečný stavem a aplikace by havarovala.