Inizia con un cane. In particolare, un pug.
Il carlino ha vari comportamenti:
public class Pug
{
private String name;
public Pug(String n)
{
name = n;
}
public String getName()
{
return name;
}
public String bark()
{
return "Arf!";
}
public boolean hasCurlyTail()
{
return true;
}
}
E tu hai un Labrador, che ha anche una serie di comportamenti.
public class Lab
{
private String name;
public Lab(String n)
{
name = n;
}
public String getName()
{
return name;
}
public String bark()
{
return "Woof!";
}
public boolean hasCurlyTail()
{
return false;
}
}
Possiamo creare carlini e laboratori:
Pug pug = new Pug("Spot");
Lab lab = new Lab("Fido");
E possiamo invocare i loro comportamenti:
pug.bark() -> "Arf!"
lab.bark() -> "Woof!"
pug.hasCurlyTail() -> true
lab.hasCurlyTail() -> false
pug.getName() -> "Spot"
lab.getName() -> "Fido"
Diciamo che gestisco un canile e ho bisogno di tenere traccia di tutti i cani che sto ospitando. Ho bisogno di conservare i miei carlini e labrador in array separati:
public class Kennel
{
Pug[] pugs = new Pug[10];
Lab[] labs = new Lab[10];
public void addPug(Pug p)
{
...
}
public void addLab(Lab l)
{
...
}
public void printDogs()
{
// Display names of all the dogs
}
}
Ma chiaramente non è ottimale. Se voglio ospitare anche alcuni barboncini, devo modificare la mia definizione di Kennel
per aggiungere un array di Poodles
. In effetti, ho bisogno di un array separato per ogni tipo di cane.
Insight: sia i carlini che i labrador (e i barboncini) sono tipi di cani e hanno lo stesso insieme di comportamenti. Cioè, possiamo dire (ai fini di questo esempio) che tutti i cani possono abbaiare, avere un nome e possono o meno avere una coda riccia. Possiamo usare un'interfaccia per definire ciò che tutti i cani possono fare , ma lasciare che siano i tipi specifici di cani a implementare quei comportamenti particolari. L'interfaccia dice "ecco le cose che tutti i cani possono fare" ma non dice come ogni comportamento è fatto.
public interface Dog
{
public String bark();
public String getName();
public boolean hasCurlyTail();
}
Quindi modifico leggermente le classi Pug
e Lab
per implementa i comportamenti di Dog
. Possiamo dire che un Pug
è un Dog
e un Lab
è un Dog
.
public class Pug implements Dog
{
// the rest is the same as before
}
public class Lab implements Dog
{
// the rest is the same as before
}
Posso ancora creare un'istanza di Pug
se Lab
s come ho fatto in precedenza, ma ora ho anche un nuovo modo per farlo:
Dog d1 = new Pug("Spot");
Dog d2 = new Lab("Fido");
Questo indica che d1
non è solo un Dog
, è specificamente un Pug
. E d2
è anche un Dog
, in particolare un Lab
.
Possiamo invocare i comportamenti e funzionano come prima:
d1.bark() -> "Arf!"
d2.bark() -> "Woof!"
d1.hasCurlyTail() -> true
d2.hasCurlyTail() -> false
d1.getName() -> "Spot"
d2.getName() -> "Fido"
Ecco dove tutto il lavoro extra paga. La classe Kennel
diventa molto più semplice. Mi serve solo un array e un metodo addDog
. Entrambi funzioneranno con qualsiasi oggetto che è un cane; cioè, gli oggetti che implementano l'interfaccia Dog
.
public class Kennel
{
Dog[] dogs = new Dog[20];
public void addDog(Dog d)
{
...
}
public void printDogs()
{
// Display names of all the dogs
}
}
Ecco come usarlo:
Kennel k = new Kennel();
Dog d1 = new Pug("Spot");
Dog d2 = new Lab("Fido");
k.addDog(d1);
k.addDog(d2);
k.printDogs();
L'ultima istruzione mostrerebbe:
Spot
Fido
Un'interfaccia ti dà la possibilità di specificare un insieme di comportamenti che tutte le classi che implementano l'interfaccia condivideranno in comune. Di conseguenza, possiamo definire variabili e collezioni (come gli array) che non devono sapere in anticipo quale tipo di oggetto specifico conserveranno, ma solo che terranno gli oggetti che implementano l'interfaccia.