JSem: 3.4
|
Un tipico esempio di interfaccia è il telecomando del televisore:
premendo vari pulsanti l'utente può regolare il volume, l'immagine, cambiare canale, ecc...
senza avere bisogno di sapere come questi metodi vengono effettivamente implementati,
in altri termini l'utente non ha bisogno di conoscere i meccanismi interni del
televisore (nè il modo in cui il telecomando comunica con i circuiti del televisore).
Un altro tipo di interfaccia, omnipresente in programmazione, sono le GUI (Graphical
User Interface), per esempio la finestra del vostro browser con i suoi vari pulsanti è
una GUI.
Più generalmente (e più astrattamente), si potrebbe dire che la lingua italiana è
un'interfaccia tra due persone (italiane!). In questo senso la nozione di interfaccia è
abbastanza vicina a quella di protocollo. Per esempio il protocollo http permette
a due computers su Internet di comunicare: in pratica i due computers si accordano sui metodi
da usare per scambiarsi le informazioni.
Un'interfaccia Java definisce dei metodi ma senza implementarli. Una classe
che implementa l'interfaccia accetta di implementare tutti i metodi dell'interfaccia,
pertanto accetta un certo tipo di comportamento ("protocollo").
Si potrebbe dire che un'interfaccia è una classe astratta i cui metodi sono tutti
astratti; questo è inesatto perchè un'interfaccia non è una classe (come vedremo,
questo è essenziale), ma rende l'idea: ogni "estensione" (nel caso specifico implementazione)
deve definire tutti i metodi dell'interfaccia.
La sintassi per dichiarare un'interfaccia è:
accesso interface Nome{
//metodi
tipo ritorno nome1(elenco parametri);
.............................
tipo ritorno nomeN(elenco parametri);
//variabili
tipo nome1 = valore;
.............
tipo nomeM = valore;
}
|
qui accesso è public o di default quando omesso
(quando l'accesso è di default, l'interfaccia è disponibile nel pacchetto in cui viene
dichiarata).
I metodi sono tutti astratti (quindi senza corpo).
Le variabili sono implicitamente final e
static, cioè non possono essere modificate dalla classe
di implementazione. Le variabili devono essere inizializzate con un valore.
Esempio:
Riprendiamo le nostre forme geometriche: vorremmo definire delle classi di forme geometriche
con certi metodi (draw e area), inoltre vorremmo che queste classi derivassero da una
classe (astratta) origine (l'origine della forma: il lato sinistro alto del rettangolo
nel quale viene designata la forma). Possiamo realizzare questo programma con
un'interfaccia InterForme e una classe Origine:
Definzione dell'interfaccia:
import java.awt.Graphics;
public interface InterForme{
// metodi
int Area();
void Draw(Graphics g);
}
|
come al solito, salvare questo file come InterForme.java e compilarlo.
La classe Origine:
class Origine{
int ox, oy;
//costruttore
Origine(int ox, int oy){
this.ox = ox;
this.oy = oy;
}
}
|
La classe RettOr che estende Origne e implementa InterForme:
import java.awt.Graphics;
class RettOr extends Origine implements InterForme{
int w,h;
//costruttore
RettOr(int ox, int oy, int w, int h){
super(ox, oy);
this.w = w;
this.h = h;
}
//implementare metodo area dell'interfaccia
public int Area(){
return w*h;
}
//implementare metodo draw dell'interfaccia
public void Draw(Graphics g){
g.drawRect(ox,oy,w,h);
}
}
|
Finalmente un'applet per testare tutto quanto:
import java.applet.*;
import java.awt.*;
public class RettOrDemo extends Applet{
RettOr R = new RettOr(10,10,30,50);
public void paint(Graphics g){
R.Draw(g);
g.drawString("L'area è: " +R.Area(),10, 90);
}
}
|
L'outpout è quello che pensate (verificatelo!).
Chiaramente, nel caso specifico, questa è una grande complicazione per il risultato
ricercato, ma vedremo altre situazioni in cui la nozione d'interfaccia semplifica
notevolmente le cose.
Osservazione: Quando si implementa un metodo di interfaccia, esso deve
essere dichiarato public.
Un'interfaccia può estenderne un'altra, la sintassi è simile a quella dell'estensione
delle classi:
interface A{
.....
}
interface B extends A{
.....
}
|
Una classe che implementa B deve definire tutti i metodi di B, vale a dire quelli di A e
quelli dichiarati in B.
Differenza tra classi astratte ed interfacce.
Come già osservato, un'interfaccia è simile ad una classe astratta con soli metodi astratti,
ma allora perchè non usare classi astratte?
Un primo motivo molto semplice: in Java una classe può estendere una sola classe. Nell'esempio
precedente se la classe RettOr estende Origine, non può estendere una classe MetForme. Una
classe però può implementare un numero qualsiasi di interfacce. Nell'esempio, RettOr non
può estendere una classe MetForme, ma può implementare un'interfaccia InterForme.
Quindi la nozione di interfaccia risolve il problema della multiereditarietà.
Ma, e questa è la seconda motivazione, lo risolve in modo particolare. In un processo di
multiereditarietà, solo le classi correlate, di una stessa famiglia, erediterano di
specifici metodi; una stessa interfaccia, invece, può essere implementata da più classi, non
necessariamente correlate. L'interfaccia ha un raggio d'azione trasversale rispetto
all'ereditarietà. In breve: "stessa interfaccia, metodi multipli", questo è un altro aspetto
del polimorfismo.
Tra le interfacce più usate in Java abbiamo, per esempio ActionListener
(per gli eventi), Runnable(per i
Threads).
|