Java: Abstraktní třídy, dědičnost a rozhraní
Z MiS
				
				
				(Rozdíly mezi verzemi)
				
																
				
				
								
				|  (Přidáno přetypování a příklad.) |  (Doplnění abstraktních tříd.) | ||
| (Nejsou zobrazeny 4 mezilehlé verze od 1 uživatele.) | |||
| Řádka 2: | Řádka 2: | ||
| == Dědičnost == | == Dědičnost == | ||
| − | *Klíčové slovo <code>extends</code>. | + | |
| − | *Třída může mít pouze jednu rodičovskou třídu! | + | * Klíčové slovo <code>extends</code>. | 
| + | * Třída může mít pouze jednu rodičovskou třídu! | ||
| * Potomek „dědí“ všechny vlastnosti předka. | * 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! | * 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 { |   public class Kamion { | ||
|       int nosnost; |       int nosnost; | ||
| Řádka 14: | Řádka 53: | ||
|       ... |       ... | ||
|   } |   } | ||
| − |   public class KamionSPrivesem extends Kamion { | + |   public class KamionSPrivesem '''extends''' Kamion { | 
|       Prives prives; |       Prives prives; | ||
|       ... |       ... | ||
| + |      '''@Override''' | ||
|       public int getNosnost() {   |       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 == | == Abstraktní třída == | ||
| * Klíčové slovo <code>abstract</code>. | * 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. | ||
| Řádka 45: | Řádka 169: | ||
| * V tom případě musíme použít přetypování. | * 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ř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); |   Prekazka prekazka = '''(Prekazka)''' this.getOneIntersectingObject(Prekazka.class); | ||
| Řádka 53: | Řádka 180: | ||
|   } |   } | ||
| − | + | * 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 == | == 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.