java open Y at-il plus à une interface que d'avoir les bonnes méthodes




jtextfield java open classroom (14)

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 suivante:

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

droite? Donc je devrais réellement 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 a les bonnes méthodes comme décrit par une interface? Ou y a-t-il d'autres utilisations d'interfaces?


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 seulement changer l'endroit où l'objet est créé». C'est un non-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 fonctionne sur cette interface. Le meilleur exemple est java.util.Collections qui 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 n'importe quelle classe qui implémente les interfaces List - pas seulement ArrayList et LinkedList , mais aussi les classes que vous écrivez vous-même, qui peuvent être implémentées d'une certaine manière java.util.Collections jamais imaginé.

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

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


Les interfaces permettent aux langages statiquement typés 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 dactylographiés dynamiquement (ou typés par le 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, il semble en contradiction avec la notion de typage fort).

Permettez-moi de démontrer:

Dans un langage typé dynamiquement (ou typé par un 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, pas plus et pas 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 makeNoise au cochon.
  5. Répète le tout en utilisant une vache, mais en l'affectant à 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 de Animal:

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

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

C'est très bien, jusqu'à ce que nous introduisons la classe des légumes. Les légumes ont le même comportement que les animaux, mais pas tous. Par exemple, les animaux et les légumes pourraient pousser, mais il est clair que les légumes ne font pas de bruit et que 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 bien dans Smalltalk car il est tapé par un canard (s'il marche comme un canard, et se transforme en charlatan - c'est un canard). Dans ce cas, lorsqu'un message est envoyé à un objet, une recherche est effectuée. la liste des méthodes du destinataire, et si une méthode de correspondance est trouvée, elle est appelée. Sinon, une sorte d'exception NoSuchMethodError est levée, mais tout est fait au moment de l'exécution.

Mais en Java, un langage typé statiquement, quel type pouvons-nous assigner à notre variable? Le maïs a besoin d'hériter de légumes, pour soutenir la croissance, mais ne peut pas hériter de l'animal, parce qu'il ne fait pas de bruit. La vache doit hériter de Animal pour soutenir makeNoise, mais elle ne peut pas hériter de Vegetable car elle ne devrait pas implémenter la récolte. Il semble que nous ayons besoin de l'héritage multiple - la capacité 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 de bord qui apparaissent (que se passe-t-il lorsque plus d'une superclasse parallèle implémente la même méthode ?, etc.)

Le long des interfaces viennent ...

Si nous faisons des classes animales et végétales, avec chaque culture adaptable, nous pouvons déclarer que notre vache est animale et que notre maïs est végétal. Nous pouvons également déclarer que les deux animaux et légumes sont cultivables. Cela nous permet d'écrire ceci pour tout développer:

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 ça nous permet de faire ça, 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 typé canard est que vous obtenez un polymorphisme vraiment sympa: tout ce qu'une classe a à faire pour fournir un comportement, c'est fournir la méthode. Tant que tout le monde joue bien, et envoie seulement des messages qui correspondent à des 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 statiquement typés fournissent une bien meilleure "programmation par contrat", car ils captureront les deux types d'erreurs ci-dessous lors 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 de l'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 donnent beaucoup des avantages du "vrai" polymorphisme, sans sacrifier la vérification du type de compilateur.


Interfaces

Il existe un certain nombre de situations dans le domaine du génie logiciel lorsqu'il est important que des groupes disparates de programmeurs acceptent un «contrat» expliquant comment leur logiciel interagit. Chaque groupe devrait être capable d'écrire son code sans aucune connaissance de la façon dont le code de l'autre groupe est écrit. D'une manière générale, les interfaces sont de tels contrats.

Par exemple, imaginez une société futuriste où des voitures robotisées commandées par ordinateur transportent des passagers dans les rues de la ville sans opérateur humain. Les constructeurs automobiles écrivent des logiciels (Java, bien sûr) qui font fonctionner l'automobile: arrêter, démarrer, accélérer, tourner à gauche, etc. Un autre groupe industriel, les fabricants d'instruments de guidage électronique, fabrique des systèmes informatiques qui reçoivent des données de position GPS (Global Positioning System) et une transmission sans fil des conditions de circulation et utilisent ces informations pour conduire la voiture.

Les constructeurs automobiles doivent publier une interface normalisée qui précise en détail les méthodes qui peuvent être utilisées pour faire bouger la voiture (n'importe quelle voiture, de n'importe quel constructeur). Les fabricants de guidage peuvent alors écrire un logiciel qui invoque les méthodes décrites dans l'interface pour commander la voiture. Aucun groupe industriel n'a besoin de savoir comment le logiciel de l'autre groupe est implémenté. En effet, chaque groupe considère son logiciel comme hautement propriétaire et se réserve le droit de le modifier à tout moment, tant qu'il continue d'adhérer à l'interface publiée.


Le but des interfaces est le polymorphisme , aussi appelé substitution de type . Par exemple, compte tenu de la méthode suivante:

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

Lorsque vous appelez la méthode scale , vous pouvez fournir n'importe quelle valeur d'un type qui implémente l'interface IBox . En d'autres termes, si Rectangle et Square implémentent tous deux IBox , vous pouvez fournir un Rectangle ou un Square chaque fois qu'un IBox est attendu.


Un bon exemple de la façon dont les interfaces sont utilisées est dans le cadre des collections. Si vous écrivez une fonction qui prend une List , cela n'a pas d'importance si l'utilisateur passe dans un Vector ou une liste de ArrayList une liste de HashList ou autre. Et vous pouvez passer cette List à n'importe quelle fonction nécessitant une interface Collection ou Iterable aussi.

Cela rend possible des fonctions telles que Collections.sort(List list) , quelle que soit la façon dont la List est implémentée.


Le but des interfaces est l' abstraction ou le découplage de la mise en œuvre.

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 comment , et vous utilisez une interface pour l'exprimer en Java.


Les interfaces sont un moyen de rendre votre code plus flexible. Ce que vous faites est ceci:

Ibox myBox=new Rectangle();

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

Ibox myBox=new OtherKindOfBox();

Une fois que vous vous y habituerez, vous constaterez que c'est une excellente façon de travailler.

Une autre raison est, par exemple, si vous voulez créer une liste de boîtes et effectuer une opération sur chacune d'elles, mais 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 boîte dans laquelle vous vous trouvez dans l'itération.


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 relâchez 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.

Lorsque les Interfaces sont utiles, c'est quand un objet doit être créé à un endroit et renvoyé à un appelant qui peut ne pas se soucier des détails d'implémentation. Transformons votre exemple IBox en une forme. Maintenant, nous pouvons 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.

Maintenant, vous pouvez utiliser une fabrique avec une variété de méthodes createShape (params) qui retourneront une forme appropriée en fonction des paramètres passés. Évidemment, l'usine 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 avez une variété d'opérations que vous devez effectuer sur vos formes. Peut-être que vous avez besoin de les trier par zone, les définir tous à une nouvelle taille, puis les afficher dans une interface utilisateur. Les formes sont toutes créées en usine et peuvent ensuite être facilement transmises aux classes Sorter, Sizer et Display. Si vous devez ajouter une classe hexagonale dans le futur, vous n'avez rien d'autre à changer que l'usine. Sans l'interface, l'ajout d'une autre forme devient un processus très compliqué.


C'est la raison pour laquelle les motifs 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 un mécanisme prêt à l'emploi pour une abstraction facile de l'instanciation. Pourtant, vous obtenez l'abstraction partout où vous ne créez pas un objet dans votre méthode, ce 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 un truc 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 le point d'avoir des interfaces est de les avoir aussi facilement que possible des types de classes).


N'oubliez pas qu'à une date ultérieure, vous pouvez prendre une classe existante et l'implémenter dans IBox , et elle sera 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 nommage ne fonctionnent pas toujours, par exemple je ne suis pas sûr que Boxable est approprié ici)


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 foo accepte ArrayList s, LinkedList s, ... pas seulement un type.

La chose la plus importante dans 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(); . La chose importante est maintenant, que myBox contient UNIQUEMENT les méthodes / champs d'IBox et non les autres méthodes (éventuellement existantes) de Rectangle .


Si vous avez CardboardBox et HtmlBox (qui implémentent tous les deux IBox), vous pouvez passer 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 d'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).


vous pourriez faire

Ibox myBox = new Rectangle();

De cette façon, vous utilisez cet objet comme Ibox et vous ne vous souciez pas que c'est vraiment Rectangle .


Interfaces où un fétiche ajouté à Java pour permettre l'héritage multiple. Les développeurs de Java ont réalisé que l'héritage multiple était une fonctionnalité "dangereuse", c'est pourquoi l'idée d'une interface est apparue.

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 des interfaces, parce que vous savez que vous pouvez étendre les interfaces et qu'elles n'auront pas de méthodes de classification ... 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.





interface