Y a-t-il plus à une interface que d'avoir les bonnes méthodes


159

Alors disons que j'ai cette interface:

public interface IBox
{
   public void setSize(int size);
   public int getSize();
   public int getArea();
  //...and so on
}

Et j'ai une classe qui l'implémente:

public class Rectangle implements IBox
{
   private int size;
   //Methods here
}

Si je voulais utiliser l'interface IBox, je ne peux pas en créer une instance, de la manière:

public static void main(String args[])
{
    Ibox myBox=new Ibox();
}

droite? Je devrais donc faire ceci:

public static void main(String args[])
{
    Rectangle myBox=new Rectangle();
}

Si c'est vrai, alors le seul but des interfaces est de s'assurer que la classe qui implémente une interface contient les méthodes correctes comme décrit par une interface? Ou y a-t-il une autre utilisation des interfaces?


2
N'oubliez pas que les interfaces ne sont pas spécifiques à Java. Tous les langages OOP les ont sous une forme ou une autre, mais pas toujours aussi explicitement définis que Java.
Herms

2
Techniquement, tous les langages POO fortement typés les ont sous une forme ou une autre. Les langues non typées ou typées canard n'ont pas un concept similaire.
Jared

1
@Jared Ne confondez-vous pas le typage fort avec le typage statique, et "non typé" avec le typage dynamique?
eljenso

Le polymorphisme peut également être accompli via des interfaces. Consultez la dernière section de cette page codenuggets.com/2014/06/20/java-interface
Jeff

Réponses:


143

Les interfaces sont un moyen de rendre votre code plus flexible. Voici ce que vous faites:

Ibox myBox=new Rectangle();

Ensuite, plus tard, si vous décidez d'utiliser un autre type de boîte (peut-être qu'il y a une autre bibliothèque, avec un meilleur type de boîte), vous changez votre code en:

Ibox myBox=new OtherKindOfBox();

Une fois que vous vous y serez habitué, vous découvrirez que c'est une excellente façon (en fait essentielle) de travailler.

Une autre raison est, par exemple, si vous souhaitez créer une liste de boîtes et effectuer une opération sur chacune d'elles, mais que vous voulez que la liste contienne différents types de boîtes. Sur chaque boîte, vous pouvez faire:

myBox.close()

(en supposant que IBox a une méthode close ()) même si la classe réelle de myBox change en fonction de la case dans laquelle vous vous trouvez dans l'itération.


54
Il n'y a rien dans cette réponse qui soit exclusif aux interfaces Java . Il en va de même pour les classes abstraites, voire les classes concrètes. Je m'attendrais à une bonne réponse pour mentionner la possibilité de mettre en œuvre plusieurs interfaces, et quand / pourquoi cela serait utile.
Rogério

16
Comment cela a-t-il été sélectionné comme réponse? C'est une brève description des raisons pour lesquelles le polymorphisme est utile, mais comme l'a dit l'affiche ci-dessus, je m'attendrais à une meilleure explication de plusieurs interfaces et plus important encore lorsqu'il est approprié d'utiliser une interface par rapport à une classe abstraite.
trevorkavanaugh

2
Cela a très peu à voir avec l'explication des interfaces et tout à voir avec les bases du polymorphisme
A-Developer-Has-No-Name

123

Ce qui rend les interfaces utiles, ce n'est pas le fait que "vous pouvez changer d'avis et utiliser une implémentation différente plus tard et avoir seulement à changer le seul endroit où l'objet est créé". Ce n'est pas un problème.

Le vrai point est déjà dans le nom: ils définissent une interface que n'importe qui peut implémenter pour utiliser tout le code qui opère sur cette interface. Le meilleur exemple est celui java.util.Collectionsqui fournit toutes sortes de méthodes utiles qui fonctionnent exclusivement sur des interfaces, telles que sort()ou reverse()pour List. Le point ici est que ce code peut maintenant être utilisé pour trier ou inverser toute classe qui implémente les Listinterfaces - pas seulement ArrayListet LinkedList, mais aussi les classes que vous écrivez vous-même, qui peuvent être implémentées d'une manière que les personnes qui ont écrit java.util.Collectionsn'ont jamais imaginé.

De la même manière, vous pouvez écrire du code qui fonctionne sur des interfaces bien connues, ou des interfaces que vous définissez, et d'autres personnes peuvent utiliser votre code sans avoir à vous demander de prendre en charge leurs classes.

Une autre utilisation courante des interfaces est pour les rappels. Par exemple, java.swing.table.TableCellRenderer , qui vous permet d'influencer la façon dont une table Swing affiche les données dans une certaine colonne. Vous implémentez cette interface, passez une instance au JTable, et à un moment donné pendant le rendu de la table, votre code sera appelé pour faire son travail.


9
C'est une très bonne réponse, j'aime bien quand vous avez donné des exemples de classes de paquets java ...
Owais Qureshi

1
J'ai aiméyou can write code that operates on well-known interfaces, or interfaces you define
Manish Kumar

Attendez un instant ... Ce qui rend les interfaces utiles, ce n'est pas [la possibilité d'utiliser n'importe quelle implémentation que vous aimez], mais plutôt [la possibilité d'utiliser n'importe quelle implémentation que vous aimez]? Mentionner le cas opposé est cependant un bon point.
Powerslave

4
@Powerslave: paraphrasez-le plus comme Ce qui rend les interfaces utiles, ce n'est pas [la possibilité d'écrire du code où vous ne devez changer qu'une seule ligne lors du changement de l'implémentation] mais plutôt [la capacité d'écrire du code là où vous ne spécifiez même pas d'implémentation à tout].
Michael Borgwardt

@MichaelBorgwardt Cela sonne bien mieux. :) Merci de clarifier!
Powerslave

119

L'une des nombreuses utilisations que j'ai lues est celle où il est difficile sans interfaces d'utilisation d'héritage multiple en Java:

class Animal
{
void walk() { } 
....
.... //other methods and finally
void chew() { } //concentrate on this
} 

Maintenant, imaginez un cas où:

class Reptile extends Animal 
{ 
//reptile specific code here
} //not a problem here

mais,

class Bird extends Animal
{
...... //other Bird specific code
} //now Birds cannot chew so this would a problem in the sense Bird classes can also call chew() method which is unwanted

Une meilleure conception serait:

class Animal
{
void walk() { } 
....
.... //other methods 
} 

Animal n'a pas la méthode chew () et est à la place placé dans une interface comme:

interface Chewable {
void chew();
}

et demandez à la classe Reptile d'implémenter ceci et non les oiseaux (puisque les oiseaux ne peuvent pas mâcher):

class Reptile extends Animal implements Chewable { } 

et en cas d'oiseaux simplement:

class Bird extends Animal { }

6
@CHEBURASHKA Et une mauvaise dénomination. Si Reptile"mâche", alors pas lui-même est "à croquer". La convention de nommage (parfois) des interfaces Whateverable ne doit être appliquée que là où cela a un sens parfait. Nommer l'interface Predatorserait plus approprié ici.
Powerslave

6
@Powerslave C'est correct à mon humble avis, un reptile est "capable de mâcher" / "à croquer". Un faucon est un prédateur mais toujours pas capable de mâcher ... juste pinailler mais «mâcher» peut être mieux défini dans la documentation de l'interface.
Madmenyo

Superbe .. Très bien expliqué. Je vous remercie..!
Gurusinghe

1
Je ne comprends pas. Tout ce qu'il semble, c'est que les interfaces sont un bon moyen de s'assurer que vous implémentez les mêmes méthodes dans chaque classe qui l'implémente, (par exemple, cela m'empêche de faire le choix stupide d'une classe Bird avec run () et une classe Dog avec runn () - ils sont tous pareils). Mais en faisant attention et en faisant en sorte que mes classes aient les mêmes formats / structures de méthodes, ne pourrais-je pas réaliser la même chose? Il semble vraiment que les interfaces s'assurent que le programmeur ne soit pas oublieux. De plus, les interfaces ne semblent pas me faire gagner du temps; J'ai encore besoin de définir la méthode dans chaque classe qui l'implémente.
Alex G

@AlexG - j'ai raconté l'une des nombreuses utilisations mec :) Il y en a plus, nous avions à peine gratté la surface pour répondre à la question de manière simple!
peevesy

47

Le but des interfaces est le polymorphisme , c'est-à-dire la substitution de type . Par exemple, étant donné la méthode suivante:

public void scale(IBox b, int i) {
   b.setSize(b.getSize() * i);
}

Lors de l'appel de la scaleméthode, vous pouvez fournir toute valeur d'un type qui implémente l' IBoxinterface. En d'autres termes, si Rectangleet les Squaredeux implémentent IBox, vous pouvez fournir un Rectangleou un Squarepartout où un IBoxest attendu.


8
Pourquoi le polymorphisme des interfaces sert-il, si je peux déjà y parvenir en Java avec le sous-classement et le remplacement de méthode?
eljenso le

1
C'est la même chose, sauf que les interfaces doivent omettre toute implémentation. Les classes peuvent donc implémenter plus d'une interface.
Apocalisp le

4
Hé, je n'ai jamais dit que Java avait une quelconque intégrité conceptuelle. La substitution de type est le but de tout sous-typage. Il se trouve que Java a plus d'un mécanisme de sous-typage, dont aucun n'est particulièrement bon.
Apocalisp le

1
Je n'ai jamais parlé non plus de l'intégrité conceptuelle. Mais passons à autre chose. Si vous pouvez mettre à l'échelle chaque IBox avec votre méthode, ne devrait-il pas s'agir d'une opération déclarée sur IBox: IBox.scale (int)?
eljenso

1
Nous ne voudrions pas coupler Integer à IBox, c'est pourquoi nous n'en faisons pas une méthode sur Integer. Et le nombre de méthodes sur une interface est décidé par la cohérence et la cohésion de l'abstraction qu'elle exprime, et non par la lourdeur de sa mise en œuvre. Quoi qu'il en soit, merci pour vos réponses Apo.
eljenso le

33

Les interfaces permettent aux langages à typage statique de prendre en charge le polymorphisme. Un puriste orienté objet insisterait sur le fait qu'un langage devrait fournir l'héritage, l'encapsulation, la modularité et le polymorphisme afin d'être un langage orienté objet complet. Dans les langages à typage dynamique - ou canard - (comme Smalltalk,) le polymorphisme est trivial; cependant, dans les langages statiquement typés (comme Java ou C #), le polymorphisme est loin d'être trivial (en fait, à première vue, il semble être en contradiction avec la notion de typage fort.)

Laissez-moi vous démontrer:

Dans un langage typé dynamiquement (ou typé canard) (comme Smalltalk), toutes les variables sont des références à des objets (rien de moins et rien de plus.) Donc, dans Smalltalk, je peux faire ceci:

|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.

Ce code:

  1. Déclare une variable locale appelée anAnimal (notez que nous NE spécifions PAS le TYPE de la variable - toutes les variables sont des références à un objet, ni plus ni moins.)
  2. Crée une nouvelle instance de la classe nommée "Pig"
  3. Affecte cette nouvelle instance de Pig à la variable anAnimal.
  4. Envoie le message makeNoiseau cochon.
  5. Répète le tout en utilisant une vache, mais en l'attribuant à la même variable exacte que le cochon.

Le même code Java ressemblerait à ceci (en supposant que Duck et Cow sont des sous-classes d'Animal:

Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();

C'est bien beau, jusqu'à ce que nous introduisions la classe Vegetable. Les légumes ont le même comportement que les animaux, mais pas tous. Par exemple, les animaux et les légumes peuvent pousser, mais il est clair que les légumes ne font pas de bruit et les animaux ne peuvent pas être récoltés.

Dans Smalltalk, nous pouvons écrire ceci:

|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.

Cela fonctionne parfaitement dans Smalltalk car il est typé canard (s'il marche comme un canard et charlatan comme un canard - c'est un canard.) Dans ce cas, lorsqu'un message est envoyé à un objet, une recherche est effectuée sur la liste des méthodes du récepteur, et si une méthode correspondante est trouvée, elle est appelée. Sinon, une sorte d'exception NoSuchMethodError est lancée - mais tout est fait au moment de l'exécution.

Mais en Java, un langage typé statiquement, quel type pouvons-nous attribuer à notre variable? Le maïs a besoin d'hériter de Vegetable, pour soutenir la croissance, mais ne peut pas hériter d'Animal, car il ne fait pas de bruit. La vache doit hériter d'Animal pour soutenir makeNoise, mais ne peut pas hériter de Vegetable car elle ne doit pas mettre en œuvre la récolte. Il semble que nous ayons besoin d' un héritage multiple - la possibilité d'hériter de plus d'une classe. Mais cela s'avère être une fonctionnalité de langage assez difficile à cause de tous les cas extrêmes qui apparaissent (que se passe-t-il lorsque plusieurs superclasses parallèles implémentent la même méthode?, Etc.)

Viennent les interfaces ...

Si nous créons des classes d'animaux et de légumes, à chaque implémentation de Growable, nous pouvons déclarer que notre vache est animale et notre maïs est légume. Nous pouvons également déclarer que les animaux et les légumes sont cultivables. Cela nous permet d'écrire ceci pour tout faire grandir:

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 cela 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 du langage de type canard est que vous obtenez un polymorphisme vraiment sympa: tout ce qu'une classe a à faire pour fournir un comportement est de fournir la méthode. Tant que tout le monde joue gentiment et n'envoie que des messages correspondant aux méthodes définies, tout va bien. L'inconvénient est que le type d'erreur ci-dessous n'est pas détecté avant l'exécution:

|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.

Les langages à typage statique fournissent une bien meilleure «programmation par contrat», car ils détecteront les deux types d'erreurs 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(); 

Alors ... pour résumer:

  1. L'implémentation d'interface vous permet de spécifier les types de choses que les objets peuvent faire (interaction) et l'héritage de classe vous permet de spécifier comment les choses doivent être faites (implémentation).

  2. Les interfaces nous offrent de nombreux avantages du "vrai" polymorphisme, sans sacrifier la vérification du type du compilateur.


2
Voici le texte de ma réponse à une autre question: stackoverflow.com/questions/379282/… . Mais, ce sont des réponses liées.
Jared

2
Alors, puis-je demander, comment un langage typé canard fait-il la distinction entre Animal.water () (qui, le fermier prude disait qu'il prend une fuite) et Plant.water () qu'il utilise pour arroser les plantes. L'ambiguïté est l'ennemi. Toute quantité de verbosité nécessaire pour surmonter l'ambiguïté est acceptable pour l'OMI.
Bill K

1
Oui ... l'ambiguïté est le nom du jeu avec des langues typées canard. Lorsque vous travaillez professionnellement dans un langage typé canard, il n'est pas rare de voir des membres (méthodes et variables) avec des noms de 50 à 100 caractères.
Jared

1
Un autre gros inconvénient des langages typés canard est l'incapacité de faire une refactorisation programmatique basée sur une analyse statique - essayez de demander à une image Smalltalk la liste de tous les appelants de votre méthode printString ... vous obtiendrez la liste de tous les appelants de TOUTES les méthodes printString. ...
Jared

... car l'appelant de Automobile # printString ne peut pas être différencié par programme de l'appelant de NearEarthOrbit # printString.
Jared

9

Normalement, les interfaces définissent l'interface que vous devez utiliser (comme son nom l'indique ;-)). Échantillon


public void foo(List l) {
   ... do something
}

Maintenant, votre fonction fooaccepte ArrayLists, LinkedLists, ... pas seulement un type.

La chose la plus importante en Java est que vous pouvez implémenter plusieurs interfaces, mais vous ne pouvez étendre qu'une seule classe! Échantillon:


class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
est possible mais

class Test extends Foo, Bar, Buz {
...
}
n'est pas!

Votre code ci - dessus pourrait aussi être: IBox myBox = new Rectangle();. L'important est maintenant que myBox contient UNIQUEMENT les méthodes / champs d'IBox et non les autres méthodes (éventuellement existantes) de Rectangle.


1
«List» est-il censé être un membre de l'interface?
Cliquez sur Upvote le

1
List est une interface dans la bibliothèque de collections java.
rmeador le

List est une interface de la bibliothèque Java standard ( java.sun.com/javase/6/docs/api/java/util/List.html ). Il l'utilise simplement pour illustrer son propos.
Michael Myers

6

Je pense que vous comprenez tout ce que font les interfaces, mais vous n'imaginez pas encore les situations dans lesquelles une interface est utile.

Si vous instanciez, utilisez et libérez un objet dans une portée étroite (par exemple, dans un appel de méthode), une interface n'ajoute vraiment rien. Comme vous l'avez noté, la classe concrète est connue.

Là où les interfaces sont utiles, c'est lorsqu'un objet doit être créé à un endroit et renvoyé à un appelant qui peut ne pas se soucier des détails de l'implémentation. Changeons votre exemple IBox en Shape. Nous pouvons maintenant avoir des implémentations de Shape telles que Rectangle, Circle, Triangle, etc. Les implémentations des méthodes getArea () et getSize () seront complètement différentes pour chaque classe concrète.

Vous pouvez maintenant utiliser une fabrique avec une variété de méthodes createShape (params) qui renverront une forme appropriée en fonction des paramètres passés. Évidemment, la fabrique saura quel type de forme est créé, mais l'appelant n'aura pas se soucier de savoir si c'est un cercle, ou un carré, ou ainsi de suite.

Maintenant, imaginez que vous devez effectuer diverses opérations sur vos formes. Vous devez peut-être les trier par zone, les définir tous à une nouvelle taille, puis les afficher dans une interface utilisateur. Les formes sont toutes créées par l'usine et peuvent ensuite être transmises très facilement aux classes Sorter, Sizer et Display. Si vous devez ajouter une classe hexagonale dans le futur, vous n'avez rien à changer sauf l'usine. Sans l'interface, l'ajout d'une autre forme devient un processus très compliqué.


6

vous pourriez faire

Ibox myBox = new Rectangle();

de cette façon, vous utilisez cet objet comme Ibox et vous ne vous souciez pas que ce soit vraiment Rectangle.


Ça veut dire qu'on pourrait écrire comme ça?! > Rectangle inst = nouveau Rectangle ();
Dr.jacky

@ Mr.Hyde Si vous voulez ajouter plus tard Squarevous auriez un problème .... si vous essayez de le faire sans interfaces, vous ne pouvez pas le garantir Squareet Rectangleavoir les mêmes méthodes ... cela peut entraîner un cauchemar lorsque vous avez une base de code plus large ... N'oubliez pas que les interfaces définissent un modèle.
Kolob Canyon du

6

POURQUOI INTERFACE ??????

Cela commence avec un chien. En particulier, un carlin .

Le carlin a différents comportements:

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; } }

Et vous avez un Labrador, qui a également un ensemble de comportements.

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; } }

Nous pouvons faire des carlins et des laboratoires:

Pug pug = new Pug("Spot"); 
Lab lab = new Lab("Fido");

Et nous pouvons invoquer leurs comportements:

pug.bark() -> "Arf!" 
lab.bark() -> "Woof!" 
pug.hasCurlyTail() -> true 
lab.hasCurlyTail() -> false 
pug.getName() -> "Spot"

Disons que je dirige un chenil et que je dois garder une trace de tous les chiens que je loge. J'ai besoin de stocker mes carlins et labradors dans des tableaux séparés :

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 } }

Mais ce n'est clairement pas optimal. Si je veux aussi héberger des caniches , je dois changer ma définition de chenil pour ajouter un tableau de caniches. En fait, j'ai besoin d'un tableau distinct pour chaque type de chien.

Perspicacité: les carlins et les labradors (et les caniches) sont des types de chiens et ils ont le même ensemble de comportements. Autrement dit, nous pouvons dire (aux fins de cet exemple) que tous les chiens peuvent aboyer, avoir un nom et peuvent ou non avoir une queue bouclée. Nous pouvons utiliser une interface pour définir ce que tous les chiens peuvent faire, mais laisser aux types spécifiques de chiens le soin de mettre en œuvre ces comportements particuliers. L'interface dit "voici les choses que tous les chiens peuvent faire" mais ne dit pas comment chaque comportement est fait.

public interface Dog 
{
public String bark(); 
public String getName(); 
public boolean hasCurlyTail(); }

Ensuite, je modifie légèrement les classes Pug et Lab pour implémenter les comportements Dog. On peut dire qu'un carlin est un chien et qu'un laboratoire est un chien.

public class Pug implements Dog {
// the rest is the same as before } 

public class Lab implements Dog { 
// the rest is the same as before 
}

Je peux toujours instancier Pugs et Labs comme je le faisais auparavant, mais maintenant j'ai aussi une nouvelle façon de le faire:

Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido");

Cela dit que d1 n'est pas seulement un chien, c'est spécifiquement un carlin. Et d2 est aussi un chien, en particulier un laboratoire. Nous pouvons invoquer les comportements et ils fonctionnent comme avant:

d1.bark() -> "Arf!" 
d2.bark() -> "Woof!" 
d1.hasCurlyTail() -> true 
d2.hasCurlyTail() -> false 
d1.getName() -> "Spot"

C'est là que tout le travail supplémentaire porte ses fruits. La classe Kennel devient beaucoup plus simple. J'ai besoin d'un seul tableau et d'une méthode addDog. Les deux fonctionneront avec n'importe quel objet qui est un chien; c'est-à-dire des objets qui implémentent l'interface Dog.

public class Kennel {
Dog[] dogs = new Dog[20]; 
public void addDog(Dog d) { ... } 
public void printDogs() {
// Display names of all the dogs } }

Voici comment l'utiliser:

Kennel k = new Kennel(); 
Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido"); 
k.addDog(d1); 
k.addDog(d2); 
k.printDogs();

Le dernier énoncé afficherait: Spot Fido

Une interface vous donne la possibilité de spécifier un ensemble de comportements que toutes les classes qui implémentent l'interface partageront en commun. Par conséquent, nous pouvons définir des variables et des collections (comme des tableaux) qui n'ont pas besoin de savoir à l'avance quel type d'objet spécifique elles contiendront, mais seulement qu'elles contiendront des objets qui implémentent l'interface.


@niranjan kurambhatti je peux faire toutes les classes pour étendre le chien mais toujours pourquoi l'interface ??
Jeeva

3

Un bon exemple de la façon dont les interfaces sont utilisées est dans le framework Collections. Si vous écrivez une fonction qui prend a List, peu importe si l'utilisateur passe un Vectorou un ArrayListou un HashListou autre. Et vous pouvez également le transmettre Listà n'importe quelle fonction nécessitant une interface Collectionou Iterable.

Cela rend des fonctions comme Collections.sort(List list)possibles, quelle que soit la façon dont le List.


3

C'est la raison pour laquelle les modèles d'usine et autres modèles de création sont si populaires en Java. Vous avez raison de dire que sans eux, Java ne fournit pas de mécanisme prêt à l'emploi pour une abstraction facile de l'instanciation. Pourtant, vous obtenez une abstraction partout où vous ne créez pas d'objet dans votre méthode, qui devrait être la majeure partie de votre code.

En passant, j'encourage généralement les gens à ne pas suivre le mécanisme "IRealname" pour nommer les interfaces. C'est une chose Windows / COM qui met un pied dans la tombe de la notation hongroise et qui n'est vraiment pas nécessaire (Java est déjà fortement typé, et tout l'intérêt d'avoir des interfaces est de les avoir aussi largement indiscernables que possible des types de classe).


1
Vous confondez le typage fort avec le typage statique.
eljenso le

3

N'oubliez pas qu'à une date ultérieure, vous pouvez prendre une classe existante et la mettre en œuvre IBox, et elle deviendra alors disponible pour tout votre code compatible avec la boîte.

Cela devient un peu plus clair si les interfaces sont nommées -able . par exemple

public interface Saveable {
....

public interface Printable {
....

etc. (les schémas de dénomination ne fonctionnent pas toujours, par exemple je ne suis pas sûr que ce Boxablesoit approprié ici)


3

le seul but des interfaces est de s'assurer que la classe qui implémente une interface a les bonnes méthodes comme décrit par une interface? Ou y a-t-il une autre utilisation des interfaces?

Je mets à jour la réponse avec de nouvelles fonctionnalités d'interface, qui ont été introduites avec la version java 8 .

Depuis la page de documentation d'Oracle sur le résumé de l'interface :

Une déclaration d'interface peut contenir

  1. signatures de méthode
  2. méthodes par défaut
  3. méthodes statiques
  4. définitions constantes.

Les seules méthodes qui ont des implémentations sont les méthodes par défaut et statiques.

Utilisations de l'interface :

  1. Pour définir un contrat
  2. Lier des classes non liées avec a une capacité (par exemple, les classes implémentant l' Serializableinterface peuvent ou non avoir une relation entre elles, sauf implémenter cette interface
  3. Pour fournir une mise en œuvre interchangeable, par exemple un modèle de stratégie
  4. Les méthodes par défaut vous permettent d'ajouter de nouvelles fonctionnalités aux interfaces de vos bibliothèques et d'assurer la compatibilité binaire avec le code écrit pour les anciennes versions de ces interfaces
  5. Organisez les méthodes d'assistance dans vos bibliothèques avec des méthodes statiques (vous pouvez conserver les méthodes statiques spécifiques à une interface dans la même interface plutôt que dans une classe distincte)

Quelques questions SE liées concernant la différence entre la classe abstraite et l' interface et des cas d'utilisation avec des exemples de travail:

Quelle est la différence entre une interface et une classe abstraite?

Comment aurais-je dû expliquer la différence entre une interface et une classe abstraite?

Consultez la page de documentation pour comprendre les nouvelles fonctionnalités ajoutées dans java 8: méthodes par défaut et méthodes statiques .


J'ai supprimé la balise java-8 car la question ne posait rien sur java-8 (et a en fait été posée longtemps avant java-8). Les tags sont pour les questions, pas pour les réponses.
Tagir Valeev

2

Le but des interfaces est l' abstraction ou le découplage de l'implémentation.

Si vous introduisez une abstraction dans votre programme, vous ne vous souciez pas des implémentations possibles. Vous êtes intéressé par ce qu'il peut faire et non par comment , et vous utilisez un interfacepour exprimer cela en Java.


Le but de toute programmation structurée est l'abstraction. Pourquoi diriez-vous que le but des interfaces est l'abstraction, puisque je peux réaliser exactement la même chose en utilisant des génériques et la composition de classes?
Apocalisp le

1
Si toute programmation structurée est une abstraction (votre revendication), alors les interfaces sont des abstractions dans cette abstraction.
eljenso le

1

Si vous avez CardboardBox et HtmlBox (qui implémentent tous les deux IBox), vous pouvez les transmettre tous les deux à n'importe quelle méthode qui accepte un IBox. Même si elles sont toutes deux très différentes et pas complètement interchangeables, les méthodes qui ne se soucient pas de "ouvrir" ou de "redimensionner" peuvent toujours utiliser vos classes (peut-être parce qu'elles se soucient du nombre de pixels nécessaires pour afficher quelque chose sur un écran).


1

Interfaces où une fétature ajoutée à java pour permettre l'héritage multiple. Les développeurs de Java ont cependant réalisé que l'héritage multiple était une fonctionnalité «dangereuse», c'est pourquoi ils ont eu l'idée d'une interface.

l'héritage multiple est dangereux car vous pourriez avoir une classe comme celle-ci:


class Box{
    public int getSize(){
       return 0;
    }
    public int getArea(){
       return 1;
    }

}

class Triangle{
    public int getSize(){
       return 1;
    }
    public int getArea(){
       return 0;
    }

}

class FunckyFigure extends Box, Triable{
   // we do not implement the methods we will used the inherited ones
}

Quelle serait la méthode qui devrait être appelée lorsque nous utilisons


   FunckyFigure.GetArea(); 

Tous les problèmes sont résolus avec les interfaces, car vous savez que vous pouvez étendre les interfaces et qu'elles n'auront pas de méthodes de classement ... bien sûr, le compilateur est sympa et vous dit si vous n'avez pas implémenté de méthodes, mais j'aime penser que c'est un effet secondaire d'une idée plus intéressante.


Vous voudrez peut-être faire une différence entre l'héritage d'implémentations multiples et l'héritage d'interfaces multiples dans votre réponse, sinon cela devient déroutant.
eljenso le

0

Voici ma compréhension de l'avantage de l'interface. Corrigez-moi si je me trompe. Imaginez que nous développons un système d'exploitation et qu'une autre équipe développe les pilotes pour certains appareils. Nous avons donc développé une interface StorageDevice. Nous en avons deux implémentations (FDD et HDD) fournies par d'autres équipes de développeurs.

Ensuite, nous avons une classe OperatingSystem qui peut appeler des méthodes d'interface telles que saveData en passant simplement une instance de classe implémentée l'interface StorageDevice.

L'avantage ici est que nous ne nous soucions pas de l'implémentation de l'interface. L'autre équipe fera le travail en implémentant l'interface StorageDevice.

package mypack;

interface StorageDevice {
    void saveData (String data);
}


class FDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to floppy drive! Data: "+data);
    }
}

class HDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to hard disk drive! Data: "+data);
    }
}

class OperatingSystem {
    public String name;
    StorageDevice[] devices;
    public OperatingSystem(String name, StorageDevice[] devices) {

        this.name = name;
        this.devices = devices.clone();

        System.out.println("Running OS " + this.name);
        System.out.println("List with storage devices available:");
        for (StorageDevice s: devices) {
            System.out.println(s);
        }

    }

    public void saveSomeDataToStorageDevice (StorageDevice storage, String data) {
        storage.saveData(data);
    }
}

public class Main {

    public static void main(String[] args) {

        StorageDevice fdd0 = new FDD();
        StorageDevice hdd0 = new HDD();     
        StorageDevice[] devs = {fdd0, hdd0};        
        OperatingSystem os = new OperatingSystem("Linux", devs);
        os.saveSomeDataToStorageDevice(fdd0, "blah, blah, blah...");    
    }
}

la même chose peut être faite avec la classe abstraite StorageDevice et les classes FDD et HDD étendant la classe StorageDevice. mais si nous utilisons une classe abstraite, nous ne pouvons pas profiter de l'héritage multiple.
Vladimir Georgiev
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.