2.5. Abstraktní třídy a metody

V některých situacích je výhodné vytvořit jedinou bázovou třídu pro více tříd odpovídajících konkrétním objektům, i když tato samotná bázová třída žádnému konkrétnímu objektu neodpovídá. Může ovšem nést některá data a poskytovat metody, které jsou odvozeným třídám společné. Taková třída se pak nazývá abstraktní a je označena klíčovým slovem abstract. Překladač jazyka Java pak zajistí, že instanci abstraktní třídy nelze operátorem new přímo vytvořit, mohou se vytvářet pouze instance konkrétních tříd.

Abstraktní třída může deklarovat některé společné metody a poskytovat jejich základní implementaci. Pokud odvozená třída takovou metodu nepředefinuje, pak se pro její instance použije implementace poskytnutá v bázové třídě.

Mohou však nastat i situace, kdy skutečně vyžadujeme, aby odvozené třídy určitou metodu vždy definovaly. Takovou metodu pak také nazýváme abstraktní a označujeme klíčovým slovem abstract, navíc u ní není uvedeno tělo a hlavička metody je zakončena středníkem. Pokud odvozená třída některou abstraktní metodu neimplementuje, musí být také označena jako abstraktní. Tím je zajištěno, že instance konkrétních tříd mají všechny metody implementované.

Příklad 2.4.
Vytvořte třídu reprezentující obecný obrazec se souřadnicemi středu, obvodem a plochou a na jejím základě definujte třídy pro kruh, obdélník a čtverec.

Obrazec je abstraktním pojmem, pro který nemá smysl definovat implicitní metodu pro výpočet obvodu a plochy. Definujeme jej tedy jako abstraktní třídu poskytující pouze souřadnice středu a abstraktní metody pro výpočet obvodu a plochy:

abstract class Obrazec { 
   public Obrazec(double x, double y) { 
      this.x = x; this.y = y; 
   } 
   public abstract double obvod(); 
   public abstract double obsah(); 
 
   protected double x; 
   protected double y;  
}

Měli bychom také doplnit metody pro získání a nastavení hodnot souřadnic středu, ale zatím se bez nich obejdeme.

Pro reprezentaci kruhu jen poněkud upravíme řešení, které jsme vytvořili dříve: class Kruh extends Obrazec {

   public Kruh(double x, double y, double r) {  
      super(x, y); 
      this.r = r;  
   } 
   public double obvod() { return 2 * 3.14159 * r; } 
   public double obsah() { return 3.14159 * r * r; } 
 
   protected double r;  
}

Třída reprezentující obdélník doplní k abstraktnímu obrazci údaje o délkách stran a implementuje výpočet obvodu a obsahu:

class Obdelnik extends Obrazec { 
   public Obdelnik(double x, double y, double a, double b) { 
      super(x, y); 
      this.a = a; this.b = b; 
   } 
   public double obvod() { return 2 * (a + b); } 
   public double obsah() { return a * b; } 
 
   protected double a; 
   protected double b; 
}

Konečně třídu reprezentující čtverec již můžeme odvodit přímo z obdélníka vhodným voláním jeho konstruktoru:

class Ctverec extends Obdelnik { 
   public Ctverec(double x, double y, double a) { 
      super(x, y, a, a); 
   } 
}

Výsledné řešení můžeme otestovat takovým způsobem, že vytvoříme pole několika obrazců a pak pro ně postupně vypočteme jednotlivé hodnoty. Povšimněte si toho, jakým způsobem můžeme inicializovat pole objektů (v Javě je pole také objekt, proto se vytváří operátorem new stejně jako jiné objekty).

class TestObrazcu { 
   public static void main(String[] args) { 
      Obrazec[] pole = new Obrazec[] {  
            new Obdelnik(0,0,2,5), 
            new Ctverec(0,0,4), 
            new Kruh(0,0,1) }; 
      for(int i = 0; i < pole.length; i++ ) { 
         System.out.print  ("pole["+i+"]:"); 
         System.out.print  (" obvod=" + pole[i].obvod()); 
         System.out.println(" obsah=" + pole[i].obsah()); 
      } 
   } 
}

2.5.1. Úlohy k řešení

  1. Definujte abstraktní rozhraní, reprezentující kontakt na osobu (adresa, e-mail, telefon apod.), a vytvořte některé jeho implementace.
  2. Inicializujte pole kontaktů několika vytvořenými instancemi a obsah tohoto pole vypište na standardní výstup.