venerdì 30 aprile 2010

Java: classi astratte

Talvolta può capitare che una classe sia eccessivamente generica ma di base a tutte le sue sottoclassi poiché implementa per queste i primi metodi di supporto. La stessa genericità della classe padre, poi, non permette a volte di implementare tutti i metodi nella stessa (ad esempio perché ogni sottoclasse implementa il metodo in questione in maniera diversa). Anziché ricorrere all'overriding del metodo si può dichiarare la classe padre come classe astratta.
Una classe astratta è una classe che non implementa tutti i metodi pur riportandone all'interno del codice la firma! Essa implementa solo i metodi più generici che coinvolgono i campi dati in comune con le sue sottoclassi. La classe, così come il metodo non definito (metodo astratto), è preceduto dall'identificatore abstract. Una classe astratta può contenere uno o più metodi astratti.
Ogni sottoclasse, allora, eredita la firma del metodo (non implementato) e può decidere se sviluppare il codice dello stesso oppure rimandare la sua scrittura al successivo livello dell'ereditarietà (il problema viene cioè passato alle successive sottoclassi). In tal caso la sottoclasse che non sviluppa il metodo incompleto (ereditato) dovrà necessariamente essere dichiarata di nuovo con l'identificatore abstract (poiché contiene nel codice un metodo astratto, quello appunto ereditato e non implementato). Non è possibile creare istanze da classi astratte, si possono tuttavia creare variabili di oggetti di classi astratte e farle riferire a oggetti delle sottoclassi!

package Elfo;

/**
* Un elfo (che non fa nulla!)
* @author Luca Petrosino
*/
public abstract class Elfo {
   public Elfo(String name) {
      this.name=name;
      this.attack=0;
      this.defense=0;
   }
   public String getName() {
      return name;
   }
   public int getAttack() {
      return attack;
   }
   public int getDefense() {
      return defense;
   }
   public void setAttack(int newAttack) {
      this.attack=newAttack;
   }
   public void setDefense(int newDefense) {
      this.defense=newDefense;
   }
   public abstract String getInfo();
   public abstract int attack();
   private String name;
   private int attack;
   private int defense;
}

La classe Elfo dell'esempio descrive una classe astratta. Implementa cioè alcuni metodi utili per le sue sottoclassi ma non precisa le istruzioni per i metodi getInfo() e attack(). Due possibili sottoclassi potrebbero essere la classe Ranger e la classe Cenn. Entrambi infatti descrivono due tipologie di elfi. Possiamo allora completare i metodi lasciati in sospeso nella classe genitore (superclasse): il metodo getInfo() restituisce una descrizione dell'elfo indicandone la tipologia, il metodo attack(), invece, restituisce l'intero rappresentativo dei danni da addebitare all'avversario (se entrambe le creature hanno ancora energia sufficiente per sferrare l'attacco).

import Elfo.*;

/**
* Un Cenn Rugosa: creatura 2/2
* @author Luca Petrosino
*/
public class Cenn extends Elfo {
   public Cenn(String name) {
      super(name);
      this.setAttack(2);
      this.setDefense(2);
   }
   public int attack() {
      if(this.getDefense()>0)
         return this.getAttack();
      else
         return 0;
   }
   public String getInfo() {
      String info="L'elfo "+this.getName()+" è un Cenn Rugosa!\n";
      return info;
   }
}

import Elfo.*;

/**
* Un Ranger della Capra Alata: creatura 3/3
* @author Luca Petrosino
*/
public class Ranger extends Elfo {
   public Ranger(String name) {
      super(name);
      this.setAttack(3);
      this.setDefense(3);
   }
   public int attack() {
      if(this.getDefense()>0)
         return this.getAttack();
      else
         return 0;
   }
   public String getInfo() {
      String info="L'elfo "+this.getName()+" è un Ranger della Capra Alata!\n";
      return info;
   }
}

Alla luce di quanto appena detto, l'istruzione Elfo c=new Elfo("Cenn Rugosa"); è errata poiché Elfo è una classe astratta e non può generare oggetti! L'istruzione Elfo c=new Cenn("Cenn Rugosa"); è invece lecita (ogni Cenn Rugosa, secondo la gerarchia dell'ereditarietà, è un elfo). In altre parole la variabile c contiene un riferimento a un oggetto della classe Cenn. Per la precisione c conterrà sempre un riferimento a un oggetto di una sottoclasse poiché la superclasse, essendo astratta, non può generare oggetti (non dimenticatelo). Pertanto anche la chiamata c.attack() è ancora valida!

Nessun commento:

Posta un commento