Derivazione ed ereditarietà

Il concetto di ereditarietà vuole che da una classe base si possano derivare infinite altre classi capaci di estenderne e specializzarne i contenuti. Nella programmazione ad oggetti esistono diversi tipi di ereditarietà, singola e multipla. il linguaggio Java non supporta l’ereditarietà multipla, per cui parleremo solo di quella singola. Data una classe base:

public class Quadrilatero {    //Attributi
    int lato1
    int lato2
    int lato3
    int lato4
    public Quadrilatero( int a, int b, int c, int d) {
        lato1 = a;
        lato2 = b;
        lato3 = c;
        lato4 = d;
    }

    public int perimetro( ) {
        return lato1 + lato2 + lato3 + lato4;
    }
}

L’estensione di questa classe base in una classe derivata specializzata avviene attraverso la seguente sintassi:

public class Rettangolo extends Quadrilatero {
    public Rettangolo (int a, int b) {
        super (a,b,a,b);
    }
}

In questo esempio possiamo notare come l’invocazione del costruttore della classe base Quadrilatero avviene attraverso la parola chiave super a cui vengono passati due volte gli stessi argomenti (trattandosi di un rettangolo). Qualora volessimo aggiungere l’attributo colore alla classe derivata la sintassi sarebbe in questo caso la seguente:

public class Rettangolo extends Quadrilatero {
    int colore;
    public Rettangolo (int a, int b, int c) {
        super (a,b,a,b);
        colore = c;
    }
} 

Notiamo che nella classe derivata é stato aggiunto un attributo il cui valore di inizializzazione é passato al metodo costruttore in cui viene invocato il metodo costruttore della classe base ed inizializzato l’attributo con l’argomento passato.

Derivazione di una classe derivata

E’ altresì possibile derivare una classe derivata creando una gerarchia di entità. Come illustrato nel diagramma seguente:

La sintassi utilizzata per derivare un classe derivata é la medesima dell’esempio precedente, con la differenza che rispetto ai 2 argomenti necessari al metodo costruttore della classe padre Rettangolo, passiamo un solo argomento, ripetendolo 2 volte, conservando in questo caso la struttura originale.

public class Quadrato extends Rettangolo {
    public Quadrato (int lato) {
        super (lato, lato);
    }
}

Overload e Override

La derivazione delle classi e la loro relativa specializzazione rendono spesso necessarie operazioni di modifica dei metodi allo scopo di ridefinirne le funzionalità. A questo scopo, é necessario che conservi lo stesso identificatore, un livello di accessibilità non inferiore al precedente e lo stesso tipo di ritorno del metodo originale.

La parola chiave final

Qualora, al contrario, volessimo impedire la derivazione di una classe, l’apposizione della parola chiave final si presta allo scopo. Viene utilizzata per indicare che l’elemento non può essere modificato, trovando così impiego anche nella dichiarazione delle costanti., o nei metodi che non possono essere ridefiniti. Nel caso dell’esempio precedente:

public final class Quadrilatero {
    /…/
}

La superclasse Object

Restando in tema di classi derivate é bene sapere che tutte le classi, compresa la classe System, derivano in realtà da una classe comune: la superclasse Object. Questa particolare classe possiede dei metodi che sono in realtà ereditati da tutte le classi e che possono essere ridefiniti in override, si tratta di:

  • Object clone( ); che clona un’oggetto.
  • boolean equals(Object); indica se due oggetti sono equivalenti;
  • void finalize( ); invocato quando l’oggetto viene cancellato;
  • int hashCode( ); restituisce un codice univoco identificativo dell’oggetto;
  • String toString( ); restituisce una descrizione del contenuto dell’oggetto in formato stringa.

Il metodo toString()

Quest’ultimo metodo risulta essere particolarmente interessante nel momento in cui volessimo conoscere l’indirizzo di memoria in cui é contenuto l’oggetto di cui vogliamo ottenere l’ubicazione.

Il metodo equals( )

Il metodo equals confronta due riferimenti a posizioni di memoria. L’invocazione del metodo accetta due argomenti, il primo, sul quale viene invocato il metodo ed il secondo viene passato come parametro. Restituisce true se due oggetti puntano alla stessa posizione di memoria. Per utilizzarlo é necessario utilizzare la seguente sintassi:

public static boolean equals (Object a, Object b)

Resta tuttavia possibile ottenere lo stesso risultato anche attraverso l’operatore di confronto (==), analizzando la sintassi del metodo originale é infatti possibile esaminarne il principio di funzionamento:

public boolean equals(Object obj) {
    return (this == obj)
}

Il metodo clone( )

Un ulteriore metodo messo a disposizione dalla classe Object, é il metodo clone, che gode di un livello di visibilità protected, che effettua una copia dell’oggetto corrente, comprese le variabili d’istanza inizializzate agli stessi valori. Essendo quest’ultimo inizializzato ad un livello di visibilità protected, andrà ridescritto in override per impostare su di esso una visibilità di tipo public.

public class Punto implements cloneable {
    int x, y;
    public Punto(int x, int y) {
        this.x = x;
        this.y = y;
}

//Override del metodo clone di Object a visibilità public

    public Object clone( ){
        Punto punto = new Punto(this.x, this.y);
        try {
            return super.clone( );
        }
        catch(CloneNotSupportedException e) {
            return null;
        }
    }
}

Occorre innanzitutto dire che per rendere possibile la clonazione é necessario dichiarare l’interfaccia “cloneable” l’oggetto che si intende clonare, altrimenti avremo un errore.

Vediamone ora l’implementazione per esteso in un metodo main:

import java.io.*;
public class CreaPunto {
    public static void main ( ) {
        Punto p, q;
        p = new Punto(3, 4);

//Casting al tipo (Punto) perché altrimenti di tipo Object

        q= (Punto) p.clone( );
    }
}

Esaminando il codice notiamo come l’invocazione del metodo clone abbia comportato un casting di tipo (Punto) appunto perché originariamente dichiarato di tipo Object.

Clonazione di un oggetto di classe derivata

Qualora volessimo clonare un oggetto di classe derivata, sarà sufficiente dichiarare il metodo Object.clone( ) con visibilità public in overload nella classe derivata per poi invocare al suo interno il metodo clone ( ) della classe basse attraverso la parola chiave super:

public class PuntoDerivato extends Punto {
    public PuntoDerivato( int x, int y) {
        super(x, y);
    }
    public Object clone {
        return super.clone( );
    }
}

Luca Scandroglio

Sono un consulente tecnico informatico, un web designer e uno sviluppatore italiano. Aiuto le aziende a dotarsi degli strumenti tecnologici e digitali per superare le sfide del mercato di oggi.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *