Les Interfaces permettent de langages statiquement typés à l'appui de polymorphisme. Orientée Objet puriste veux insister sur le fait qu'une langue doit fournir à l'héritage, l'encapsulation, modularité et le polymorphisme pour être un complet langage Orienté Objet. Dans dynamiquement typés ou de canard tapé langues (comme Smalltalk,) le polymorphisme est trivial; toutefois, dans les langages à typage statique (comme Java ou C#,) le polymorphisme est loin d'être négligeable (en fait, sur la surface, il semble être en contradiction avec la notion de typage fort.)
Laissez-moi vous montrer:
Dans un dynamiquement typés (ou de canard tapé) langue (Smalltalk), toutes les variables sont des références à des objets (rien de moins et rien de plus.) Donc, en Smalltalk, je peux faire ceci:
|anAnimal|
anAnimal := Pig new.
anAnimal makeNoise.
anAnimal := Cow new.
anAnimal makeNoise.
Ce code:
- Déclare une variable locale appelée anAnimal (notez que nous N'avons PAS de spécifier le TYPE de la variable - toutes les variables sont des références à un objet, rien de plus et rien de moins.)
- Crée une nouvelle instance de la classe nommée "Cochon"
- Attribue cette nouvelle instance de Porc à la variable anAnimal.
- Envoie le message
makeNoise
pour le porc.
- Répète le tout à l'aide d'une vache, mais en l'assignant à la même variable que le Cochon.
Le même code Java ressemblerait à quelque chose comme ceci (en faisant l'hypothèse que le Canard et la Vache sont des sous-classes d'Animaux:
Animal anAnimal = new Pig();
duck.makeNoise();
anAnimal = new Cow();
cow.makeNoise();
C'est bien beau tout ça, jusqu'à ce que nous introduisons la classe de Légumes. Les légumes ont certains de le même comportement de l'Animal, mais pas tous. Par exemple, à la fois Animales et Végétales pourraient être en mesure de croître, mais à l'évidence les légumes ne pas faire de bruit et les animaux ne peuvent pas être récoltés.
En Smalltalk, nous pouvons écrire ceci:
|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.
aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.
Cela fonctionne parfaitement bien dans Smalltalk parce que c'est le canard tapé (si ça marche comme un canard, et des charlatans comme un canard, c'est un canard.) Dans ce cas, lorsqu'un message est envoyé à un objet, une recherche est effectuée sur le récepteur de la méthode de la liste, et si une méthode de correspondance est trouvée, elle est appelée. Si non, une sorte de NoSuchMethodError exception est levée, mais tout se fait au moment de l'exécution.
Mais en Java, un langage statiquement typé, ce type de peut-on attribuer à notre variable? Le maïs doit hériter de Légumes, à l'appui de croître, mais ne peut pas hériter de l'Animal, car il ne font pas de bruit. La vache a besoin d'hériter de l'Animal à l'appui makeNoise, mais ne peut pas hériter de Légumes car il ne faut pas mettre en œuvre la récolte. Il semble que nous ayons besoin de l'héritage multiple - la possibilité d'hériter de plus d'une classe. Mais qui s'avère être assez difficile pour le langage, parce que de tous les cas de bord que les pop-up (ce qui se produit lorsque plus d'un parallèle de la superclasse de mettre en œuvre la même méthode?, etc.)
Que les interfaces...
Si nous faisons Animales et Végétales classes, avec la mise en œuvre de chaque Cultivables, nous pouvons déclarer que notre Vache est l'Animal et notre Maïs est un Légume. On peut aussi déclarer qu'à la fois Animales et Végétales sont Cultivables. Qui nous permet d'écrire ce à croître tout:
List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());
for(Growable g : list) {
g.grow();
}
Et il nous permet de faire cela, de faire des bruits d'animaux:
List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
a.makeNoise();
}
L'avantage pour le canard de type de langage est que vous obtenez vraiment sympa polymorphisme: tous d'une classe a à faire pour fournir comportement est de fournir la méthode. Tant que tout le monde joue de nice, et envoie uniquement les messages qui correspondent aux méthodes définies, tout est bon. L'inconvénient est que le type d'erreur ci-dessous n'est pas pris jusqu'à l'exécution:
|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.
Statiquement typé langues fournir beaucoup mieux "de la programmation par contrat", parce qu'ils vont attraper les deux types d'erreur ci-dessous au moment de la compilation:
// Compiler error: Corn cannot be cast to Animal.
Animal farmObject = new Corn();
farmObject makeNoise();
--
// Compiler error: Animal doesn't have the harvest message.
Animal farmObject = new Cow();
farmObject.harvest();
Donc....pour résumer:
L'implémentation de l'Interface vous permet de spécifier quels sont les types de choses que les objets peuvent faire (interaction) et l'héritage de Classe permet de spécifier la façon dont les choses devraient être faites (mise en œuvre).
Les Interfaces de nous donner de nombreux avantages de la "vraie" polymorphisme, sans sacrifier le compilateur le type de la vérification.