Hinter einer Schnittstelle steckt mehr als die richtigen Methoden

Lesezeit: 13 Minuten

Benutzer-Avatar
Ali

Nehmen wir also an, ich habe diese Schnittstelle:

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

Und ich habe eine Klasse, die es implementiert:

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

Wenn ich die Schnittstelle IBox verwenden wollte, kann ich auf folgende Weise keine Instanz davon erstellen:

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

Rechts? Also müsste ich das eigentlich machen:

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

Wenn das stimmt, dann besteht der einzige Zweck von Schnittstellen darin, sicherzustellen, dass die Klasse, die eine Schnittstelle implementiert, die richtigen Methoden enthält, wie sie von einer Schnittstelle beschrieben werden? Oder gibt es eine andere Verwendung von Schnittstellen?

  • Denken Sie daran, dass Schnittstellen nicht spezifisch für Java sind. Alle OOP-Sprachen haben sie in irgendeiner Form, wenn auch nicht immer so explizit definiert wie Java.

    – Hermes

    2. Februar 2009 um 21:19 Uhr

  • Technisch gesehen haben alle stark typisierten OOP-Sprachen sie in irgendeiner Form. Nicht typisierte oder ententypisierte Sprachen haben kein ähnliches Konzept.

    – Jared

    2. Februar 2009 um 21:24 Uhr

  • @Jared Verwechseln Sie nicht starke Typisierung mit statischer Typisierung und “nicht typisiert” mit dynamisch typisiert?

    – eljenso

    4. Februar 2009 um 10:47 Uhr

  • Polymorphismus kann auch über Schnittstellen erreicht werden. Überprüfen Sie den letzten Abschnitt dieser Seite codenuggets.com/2014/06/20/java-interface

    – Jeff

    1. Juli 2014 um 4:32 Uhr

  • stackoverflow.com/a/24436493/1286942

    – Jo Smo

    27. Juli 2015 um 3:27 Uhr

Der Zweck von Schnittstellen ist Polymorphismusauch bekannt Art der Substitution. Zum Beispiel die folgende Methode gegeben:

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

Beim Anrufen der scale -Methode können Sie einen beliebigen Wert angeben, der von einem Typ ist, der die implementiert IBox Schnittstelle. Mit anderen Worten, wenn Rectangle und Square beide implementieren IBoxkönnen Sie entweder a Rectangle oder ein Square wo immer ein IBox wird erwartet.

  • Warum ist der Zweck von Interfaces Polymorphismus, wenn ich das bereits in Java mit Unterklassenbildung und Methodenüberschreibung erreichen kann?

    – eljenso

    4. Februar 2009 um 11:13 Uhr

  • Es ist dasselbe, außer dass Schnittstellen jede Implementierung weglassen müssen. Klassen können also mehr als eine Schnittstelle implementieren.

    – Apokalypse

    4. Februar 2009 um 14:25 Uhr

  • Hey, ich habe nie gesagt, dass Java irgendeine Art von konzeptioneller Integrität hat. Die Typsubstitution ist der Zweck aller Subtypisierung. Java hat mehr als einen Subtyping-Mechanismus, von denen keiner besonders gut ist.

    – Apokalypse

    4. Februar 2009 um 17:55 Uhr

  • Ich habe auch nie etwas über konzeptionelle Integrität gesagt. Aber lass uns weitermachen. Wenn Sie jede IBox mit Ihrer Methode skalieren können, sollte es nicht eine auf IBox deklarierte Operation sein: IBox.scale(int)?

    – eljenso

    4. Februar 2009 um 18:39 Uhr

  • Wir würden Integer nicht an IBox koppeln wollen, deshalb machen wir es nicht zu einer Methode für Integer. Und die Anzahl der Methoden auf einer Schnittstelle wird durch die Konsistenz und Kohäsion der Abstraktion bestimmt, die sie ausdrückt, und nicht, wie umständlich es wäre, sie zu implementieren. Wie auch immer, danke für deine Antworten Apo.

    – eljenso

    4. Februar 2009 um 22:00 Uhr

  • @CHEBURASHKA Und schlechte Benennung. Ob Reptile “kaut”, als nicht selbst “kaubar” ist. Die Konvention der (manchmal) Benennung von Schnittstellen Was auch immer sollte nur dort eingesetzt werden, wo es absolut sinnvoll ist. Benennen der Schnittstelle Predator wäre hier besser geeignet.

    – Machtsklave

    31. Oktober 2014 um 11:13 Uhr

  • @Powerslave Es ist meiner Meinung nach richtig, ein Reptil kann “kauen” / “kauen”. Ein Falke ist ein Raubtier, aber immer noch nicht in der Lage zu kauen … nur Erbsenzählerei, aber “kaubar” kann in der Dokumentation der Schnittstelle besser definiert werden.

    – Madmenyo

    12. November 2014 um 19:00 Uhr

  • Super.. Sehr gut erklärt. Danke..!

    – Gurusinghe

    2. September 2015 um 0:32 Uhr


  • Ich verstehe es nicht. Alles, was es scheint, ist, dass Schnittstellen ein guter Weg sind, um sicherzustellen, dass Sie in jeder Klasse, die sie implementiert, dieselben Methoden implementieren (z. B. damit ich nicht die dumme Wahl zwischen einer Bird-Klasse mit run() und einer Dog-Klasse treffe). mit runn() – sie sind alle gleich). Aber könnte ich nicht dasselbe erreichen, wenn ich darauf achte und meine Klassen die gleichen Methodenformate / -strukturen habe? Scheint wirklich so, als ob Schnittstellen nur sicherstellen, dass der Programmierer nicht vergesslich ist. Außerdem scheinen mir Schnittstellen keine Zeit zu sparen; Ich muss noch die Methode in jeder Klasse definieren, die sie implementiert.

    – Alex G

    28. April 2016 um 0:50 Uhr

  • @AlexG – ich habe es einem der vielen Verwendungszwecke gesagt, Alter 🙂 Es gibt noch mehr, wir hatten kaum an der Oberfläche gekratzt, um die Frage auf einfache Weise zu beantworten!

    – ärgerlich

    21. Juni 2016 um 8:13 Uhr


Schnittstellen sind eine Möglichkeit, Ihren Code flexibler zu gestalten. Was Sie tun, ist Folgendes:

Ibox myBox=new Rectangle();

Wenn Sie später entscheiden, dass Sie eine andere Art von Box verwenden möchten (vielleicht gibt es eine andere Bibliothek mit einer besseren Art von Box), ändern Sie Ihren Code zu:

Ibox myBox=new OtherKindOfBox();

Sobald Sie sich daran gewöhnt haben, werden Sie feststellen, dass es eine großartige (eigentlich wesentliche) Art zu arbeiten ist.

Ein anderer Grund ist zum Beispiel, wenn Sie eine Liste von Boxen erstellen und für jede eine bestimmte Operation ausführen möchten, die Liste jedoch verschiedene Arten von Boxen enthalten soll. Auf jeder Box könnten Sie Folgendes tun:

myBox.close()

(vorausgesetzt, IBox hat eine close()-Methode), obwohl sich die tatsächliche Klasse von myBox ändert, je nachdem, an welcher Box Sie sich in der Iteration befinden.

  • Es gibt nichts in dieser Antwort, das exklusiv ist Java-Schnittstellen. Dasselbe gilt gleichermaßen für abstrakte oder sogar konkrete Klassen. Ich würde eine gute Antwort erwarten, um die Fähigkeit zur Implementierung zu erwähnen mehrere Schnittstellen, und wann/warum das sinnvoll wäre.

    – Rogerio

    13. Oktober 2010 um 20:04 Uhr

  • Wie wurde dies als Antwort ausgewählt? Es ist eine kurze Beschreibung, warum Polymorphismus nützlich ist, aber wie das Poster oben sagte, würde ich eine bessere Erklärung mehrerer Schnittstellen erwarten und, was noch wichtiger ist, wenn es angebracht ist, eine Schnittstelle im Vergleich zu einer abstrakten Klasse zu verwenden.

    – trevorkavanaugh

    28. Mai 2014 um 21:31 Uhr

  • Das hat sehr wenig mit der Erklärung von Schnittstellen zu tun, sondern alles mit den Grundlagen der Polymorphie

    – IsraelU

    4. Dezember 2016 um 19:59 Uhr

Schnittstellen ermöglichen es statisch typisierten Sprachen, Polymorphismus zu unterstützen. Ein objektorientierter Purist würde darauf bestehen, dass eine Sprache Vererbung, Kapselung, Modularität und Polymorphie bereitstellen sollte, um eine voll funktionsfähige objektorientierte Sprache zu sein. In dynamisch typisierten – oder ducktypisierten – Sprachen (wie Smalltalk) ist Polymorphismus trivial; In statisch typisierten Sprachen (wie Java oder C#) ist Polymorphismus jedoch alles andere als trivial (tatsächlich scheint es oberflächlich betrachtet im Widerspruch zum Begriff der starken Typisierung zu stehen).

Lassen Sie mich demonstrieren:

In einer dynamisch typisierten (oder Enten-typisierten) Sprache (wie Smalltalk) sind alle Variablen Verweise auf Objekte (nicht weniger und nicht mehr). In Smalltalk kann ich also Folgendes tun:

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

anAnimal := Cow new.
anAnimal makeNoise.

Dieser Code:

  1. Deklariert eine lokale Variable namens anAnimal (beachten Sie, dass wir NICHT den TYP der Variablen angeben – alle Variablen sind Verweise auf ein Objekt, nicht mehr und nicht weniger.)
  2. Erstellt eine neue Instanz der Klasse namens „Pig“
  3. Weist diese neue Instanz von Pig der Variablen anAnimal zu.
  4. Sendet die Nachricht makeNoise zum Schwein.
  5. Wiederholt das Ganze mit einer Kuh, weist ihr aber genau dieselbe Variable wie das Schwein zu.

Derselbe Java-Code würde in etwa so aussehen (unter der Annahme, dass Duck und Cow Unterklassen von Animal sind:

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

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

Das ist alles schön und gut, bis wir die Klasse Gemüse einführen. Gemüse hat einiges des gleichen Verhaltens wie Tier, aber nicht alles. Zum Beispiel könnten sowohl Tier als auch Gemüse wachsen, aber Gemüse macht offensichtlich keinen Lärm und Tiere können nicht geerntet werden.

In Smalltalk können wir Folgendes schreiben:

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

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

Dies funktioniert in Smalltalk perfekt, weil es Ententyp ist (wenn es wie eine Ente geht und wie eine Ente quakt, ist es eine Ente.) In diesem Fall wird eine Suche durchgeführt, wenn eine Nachricht an ein Objekt gesendet wird die Methodenliste des Empfängers, und wenn eine passende Methode gefunden wird, wird sie aufgerufen. Wenn nicht, wird eine Art NoSuchMethodError-Ausnahme geworfen – aber das alles geschieht zur Laufzeit.

Aber in Java, einer statisch typisierten Sprache, welchen Typ können wir unserer Variablen zuweisen? Mais muss von Gemüse erben, um das Wachstum zu unterstützen, kann aber nicht von Tier erben, da er keinen Lärm macht. Cow muss von Animal erben, um makeNoise zu unterstützen, kann aber nicht von Vegetable erben, da es Harvest nicht implementieren sollte. Es sieht so aus, als müssten wir Mehrfachvererbung – die Fähigkeit, von mehr als einer Klasse zu erben. Aber das stellt sich aufgrund all der Randfälle, die auftauchen, als ziemlich schwierige Sprachfunktion heraus (was passiert, wenn mehr als eine parallele Oberklasse dieselbe Methode implementiert? usw.)

Hinzu kommen Schnittstellen…

Wenn wir Tier- und Gemüseklassen erstellen, können wir mit jeder Implementierung von Growable erklären, dass unsere Kuh ein Tier und unser Mais ein Gemüse ist. Wir können auch erklären, dass sowohl Tier als auch Gemüse anbaufähig sind. Das lässt uns Folgendes schreiben, um alles wachsen zu lassen:

List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}

Und es lässt uns dies tun, um Tiergeräusche zu machen:

List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
  a.makeNoise();
}

Der Vorteil der Entensprache besteht darin, dass Sie einen wirklich schönen Polymorphismus erhalten: Alles, was eine Klasse tun muss, um Verhalten bereitzustellen, ist die Bereitstellung der Methode. Solange alle nett spielen und nur Nachrichten senden, die mit definierten Methoden übereinstimmen, ist alles gut. Der Nachteil ist, dass die folgende Art von Fehler erst zur Laufzeit abgefangen wird:

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

Statisch typisierte Sprachen bieten eine viel bessere “Programmierung nach Vertrag”, da sie die beiden folgenden Fehlerarten zur Kompilierzeit abfangen:

// 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(); 

Also …. zusammenfassend:

  1. Mit der Schnittstellenimplementierung können Sie angeben, was Objekte tun können (Interaktion), und mit der Klassenvererbung können Sie angeben, wie die Dinge ausgeführt werden sollen (Implementierung).

  2. Schnittstellen geben uns viele der Vorteile von “echtem” Polymorphismus, ohne die Überprüfung des Compilertyps zu opfern.

  • Es gibt nichts in dieser Antwort, das exklusiv ist Java-Schnittstellen. Dasselbe gilt gleichermaßen für abstrakte oder sogar konkrete Klassen. Ich würde eine gute Antwort erwarten, um die Fähigkeit zur Implementierung zu erwähnen mehrere Schnittstellen, und wann/warum das sinnvoll wäre.

    – Rogerio

    13. Oktober 2010 um 20:04 Uhr

  • Wie wurde dies als Antwort ausgewählt? Es ist eine kurze Beschreibung, warum Polymorphismus nützlich ist, aber wie das Poster oben sagte, würde ich eine bessere Erklärung mehrerer Schnittstellen erwarten und, was noch wichtiger ist, wenn es angebracht ist, eine Schnittstelle im Vergleich zu einer abstrakten Klasse zu verwenden.

    – trevorkavanaugh

    28. Mai 2014 um 21:31 Uhr

  • Das hat sehr wenig mit der Erklärung von Schnittstellen zu tun, sondern alles mit den Grundlagen der Polymorphie

    – IsraelU

    4. Dezember 2016 um 19:59 Uhr

WARUM SCHNITTSTELLE??????

Es beginnt mit einem Hund. Insbesondere ein Mops.

Der Mops hat verschiedene Verhaltensweisen:

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

Und Sie haben einen Labrador, der auch eine Reihe von Verhaltensweisen hat.

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

Wir können ein paar Möpse und Labore machen:

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

Und wir können ihr Verhalten aufrufen:

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

Nehmen wir an, ich betreibe eine Hundehütte und muss den Überblick über alle Hunde behalten, die ich beherberge. ich Ich muss meine Möpse und Labradore in separaten Arrays aufbewahren:

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

Aber das ist eindeutig nicht optimal. Ob Ich möchte ein paar Pudel unterbringenAußerdem muss ich meine Kennel-Definition ändern, um eine Reihe von Pudeln hinzuzufügen. Tatsächlich brauche ich für jede Art von Hund ein separates Array.

Einsicht: Sowohl Möpse als auch Labradore (und Pudel) sind Arten von Hunden und sie haben die gleichen Verhaltensweisen. Das heißt, wir können (für die Zwecke dieses Beispiels) sagen, dass alle Hunde bellen können, einen Namen haben und einen lockigen Schwanz haben können oder nicht. Wir können eine Schnittstelle verwenden, um zu definieren, was alle Hunde tun können, aber es den spezifischen Hundetypen überlassen, diese bestimmten Verhaltensweisen umzusetzen. Die Benutzeroberfläche sagt “Hier sind die Dinge, die alle Hunde tun können”, sagt aber nicht, wie jedes Verhalten ausgeführt wird.

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

Dann ändere ich die Klassen Pug und Lab leicht, um das Hundeverhalten zu implementieren. Wir können sagen, dass ein Mops ein Hund und ein Labrador ein Hund ist.

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

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

Ich kann Pugs und Labs immer noch wie zuvor instanziieren, aber jetzt bekomme ich auch eine neue Möglichkeit, dies zu tun:

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

Dies besagt, dass d1 nicht nur ein Hund ist, sondern speziell ein Mops. Und d2 ist auch ein Hund, speziell ein Labrador. Wir können die Verhaltensweisen aufrufen und sie funktionieren wie zuvor:

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

Hier zahlt sich die Mehrarbeit aus. Die Kennel-Klasse wird viel einfacher. Ich brauche nur ein Array und eine addDog-Methode. Beide funktionieren mit jedem Objekt, das ein Hund ist; das heißt, Objekte, die die Dog-Schnittstelle implementieren.

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

So verwenden Sie es:

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

Die letzte Anweisung würde anzeigen: Spot Fido

Eine Schnittstelle gibt Ihnen die Möglichkeit, eine Reihe von Verhaltensweisen anzugeben, die alle Klassen, die die Schnittstelle implementieren, gemeinsam haben. Folglich können wir Variablen und Sammlungen (z. B. Arrays) definieren, die nicht im Voraus wissen müssen, welche Art von spezifischem Objekt sie enthalten werden, nur dass sie Objekte enthalten, die die Schnittstelle implementieren.

  • @niranjan kurambhatti Ich kann alle Klassen machen, um den Hund zu erweitern, aber warum immer noch Interface?

    – Jeeva

    9. Mai 2019 um 11:46 Uhr

1006680cookie-checkHinter einer Schnittstelle steckt mehr als die richtigen Methoden

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy