interfacce

definire aspetti comuni

Capita piuttosto spesso che classi diverse abbiano in realtà funzionalità comuni e che però non si possa far sì che queste siano sottoclassi di una classe comune; altre volte la creazione di una classe comune non è neanche desiderabile. Proviamo a vedere un esempio.

Vogliamo realizzare un programma che gestisca delle squadre di atletica, tra le cose che deve fare c'è la gestione di una rubrica telefonica che mi permetta di visualizzare i numeri di telefono di diversi soggetti con cui la mia società sportiva ha contatto: gli Atleti e le Squadre.

Di una Squadra mi interessa memorizzare il colore della maglia, il nome e il numero di telefono; di un Atleta mi interessa memorizzare il numero di telefono, il nome e il tempo migliore sui 100m.

Il mio programma ha anche una classe che permette di visualizzare i numeri di telefono che si chiama VisualizzatoreRubrica e per adesso questa classe ha due soli metodi pubblici:

class VisualizzatoreRubrica {
	/* altri metodi e proprietà */
	
    public void visualizzaSquadra(Squadra s){
        campoNome.setText(s.getNome());
        ...
    }
    public void visualizzaAtleta(Atleta a){
        campoNome.setText(a.getNome());
        ...
    }
}

E se in futuro venissero create altre entità (ad esempio "Allenatore" o "Palestra") che io vorrò visualizzare? Dovrei di nuovo andare a modificare VisualizzatoreRubrica. Questa cosa è tutt'altro che comoda perché ad esempio ad occuparsi di tale interfaccia grafica potrebbe essere un programmatore mentre a gestire le classi Squadra, Atleta, Allenatore e simili è un altro programmatore o sono addirittura più di uno. Come posso fare in modo che la mia interfaccia grafica funzioni senza doverla sempre modificare?

Per risolvere questo problema ci vengono in aiuto le interfacce. L'idea è questa: entrambi gli oggetti (Atleta e Squadra) rappresentano, oltre alle loro peculiari caratteristiche, anche una voce di rubrica. In particolare nel contesto di VisualizzatoreRubrica non ci interessa nessuna altra informazione (non importa il tempo sui 100m per esempio).

Una interfaccia è un tipo astratto (cioè di cui non si possono creare istanze usando new) usato per specificare il comportamento che deve avere una classe che lo implementa.

Quindi dalla definizione si capisce che da qualche parte l'interfaccia viene dichiarata e da qualche altra implementata.

Dichiarazione di una interfaccia

Una interfaccia si dichiara in un file come una normale classe ma sostituendo class con interface.

Questa qui sotto è la nostra interfaccia:

public interface ElementoRubrica {
    public String getNome();
    public String getNumero();
}

Cosa ha di strano questo file?

i metodi sono publiclo possono essere come nelle classi manca il corpo dei metodi giusto!

Le interfacce a differenza delle classi non dicono come svolgere le azioni (cosa che nelle classi si fa nel corpo dei metodi) ma soltanto quali funzionalità un oggetto deve avere.

Implementazione di una interfaccia

Adesso dobbiamo dichiarare che il nostro Atleta si comporta anche come un ElementoRubrica: dobbiamo cioè implementare l'interfaccia usando la parola chiave implements.

public class Atleta implements ElementoRubrica {
    private String nome;
    private String numero;
    private double tempoCentoMetri;
    
    /* tutti gli altri metodi */
    
    public String getNome() {
        return nome;
    }

    public String getNumero() {
        return numero;
    }
}

Sebbene in Java sia possibile estendere una sola classe si possono però implementare quante interfacce si vuole (separandone i nomi da ","). Bisogna però tener presente che una interfaccia non può essere direttamente instanziata cioè non posso creare un oggetto di tipo ElementoRubrica usando new perché nella definizione dell'interfaccia io non ho definito il suo comportamento (manca il corpo dei metodi).

Il vantaggio a questo punto è questo: nella mia classe VisualizzatoreRubrica posso ignorare quale oggetto particolare devo visualizzare, mi interessa soltanto che implementi l'opportuna interfaccia ElementoRubrica. Il nostro codice diventa:

class VisualizzatoreRubrica {
    /* altri metodi e variabili */
    void visualizza(ElementoRubrica e){
	    campoNome.setText(e.getNome());
	    ...
    }
}

Adesso al metodo visualizza() non interessa quale tipo di oggetto viene passato ma soltanto che implementi l'interfaccia ElementoRubrica. Se volessi implementare questa interfaccia anche nell'oggetto Allenatore la mia interfaccia grafica non avrebbe bisogno di nessuna modifica e funzionerebbe comunque.