giovedì 29 aprile 2010

Java: ereditarietà

In Java è possibile estendere una classe e aggiungere alla nuova classe generata altre funzionalità. La classe di partenza (quella da estendere) prende il nome di superclasse, la scelta di questo nome è secondo me infelice poiché lascia intendere che essa sia dotata di qualcosa in più! E' invece vero il contrario! La classe estesa (detta sottoclasse), infatti, spesso aggiunge nuovi campi e nuovi metodi alla superclasse che è quindi generica (rappresenta un insieme più generico). Per questo motivo suggerisco di adottare il termine classe genitore per la superclasse e classe figlia per la classe estesa. La parola chiave da usare per estendere una classe è il modificatore extends. A breve vedremo come estendere la seguente classe:

package Elfo;

/**
 * Un elfo (che non fa nulla!)
 * @author Luca Petrosino
 */
public class Elfo {
   public Elfo(String name) {
      this.name=name;
      this.enemy=5;
   }
   public String getName() {
      return name;
   }
   public int getEnemy() {
      return enemy;
   }
   public void setEnemy(int newEnemy) {
      this.enemy=newEnemy;
   }
   private String name;
   private int enemy;
}

Realizzeremo dalla classe Elfo una nuova classe, la classe Wizard (che almeno fa qualcosa, attacca!). La nuova classe eredita dalla classe genitore tutti i metodi (pubblici) in essa già implementati. E' poi possibile riscrivere questi metodi per adeguarli ai nuovi campi dati che si aggiungono. Attenzione, in tal caso, per invocare il metodo della classe genitore che si sta riscrivendo (override) è opportuno specificare la chiamata con l'identificatore super! Se ciò non viene fatto il metodo che si sta riscrivendo e che vorrebbe chiamare il metodo della classe genitore (per poi aggiungere al valore restituito qualcosa di nuovo) in realtà chiama se stesso! Se non si verificano ambiguità con le chiamate ai metodi della classe genitore si può usare l'identificatore this, che chiama il metodo della classe, anche quelli ereditati (consiglio tuttavia di usare sempre super, in questo modo si evidenzia ancora di più la provenienza del metodo). Il costruttore della nuova classe dovrà infine invocare il costruttore della classe genitore (passando a questo i parametri necessari, quelli cioè che servono alla classe genitore) e procedere quindi all'assegnazione dei valori per i nuovi campi. Ancora una volta, la chiamata al costruttore della classe genitore avviene con l'identificatore super.

import Elfo.*;

/**
 * Un mago (che fa qualcosa!)
 * @author Luca Petrosino
 */
public class Wizard extends Elfo {
   public Wizard(String name,int mana) {
      super(name);
      this.setEnemy(10);
      this.mana=5;
      this.attack=2;
   }
   public int getMana() {
      return mana;
   }
   public int getAttack() {
      return attack;
   }
   public void setMana(int newMana) {
      this.mana=newMana;
   }
   public void setAttack(int newAttack) {
      this.attack=newAttack;
   }
   public int attack() {
      if (mana>0) {
         mana-=attack;
         if (mana<0) super.setEnemy(mana);
         return attack;
      }
      else return 0;
   }
   private int mana;
   private int attack;
}


E' possibile invocare il metodo attack() solo su oggetti che appartengono alla classe Wizard. L'istruzione if (mana<0) super.setEnemy(mana) è necessaria poiché l'accesso ai campi dati privati della classe genitore è vietato, esso può avvenire solo attraverso l'interfaccia pubblica della classe!

Nessun commento:

Posta un commento