C'è di più in un'interfaccia che avere i metodi corretti


159

Quindi diciamo che ho questa interfaccia:

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

E ho una classe che la implementa:

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

Se volessi usare l'interfaccia IBox, in realtà non posso crearne un'istanza, nel modo:

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

giusto? Quindi dovrei davvero fare questo:

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

Se questo è vero, allora l'unico scopo delle interfacce è assicurarsi che la classe che implementa un'interfaccia abbia i metodi corretti in essa descritti da un'interfaccia? O c'è qualche altro uso delle interfacce?


2
Ricorda, le interfacce non sono specifiche di Java. Tutti i linguaggi OOP li hanno in un modo o nell'altro, anche se non sempre definiti in modo esplicito come Java.
Herms,

2
Tecnicamente, tutti i linguaggi OOP fortemente tipizzati li hanno in un modo o nell'altro. Le lingue non tipizzate o dattiloscritte, non hanno un concetto simile.
Jared,

1
@Jared Non stai confondendo la tipizzazione forte con quella statica e "non tipizzata" con la digitazione dinamica?
eljenso,

Il polimorfismo può essere realizzato anche tramite interfacce. Controlla l'ultima sezione di questa pagina codenuggets.com/2014/06/20/java-interface
Jeff

Risposte:


143

Le interfacce sono un modo per rendere il tuo codice più flessibile. Quello che fai è questo:

Ibox myBox=new Rectangle();

Quindi, in seguito, se decidi di voler utilizzare un diverso tipo di box (forse c'è un'altra libreria, con un tipo migliore di box), cambi il codice in:

Ibox myBox=new OtherKindOfBox();

Una volta che ti ci abitui, scoprirai che è un ottimo modo (davvero essenziale) di lavorare.

Un altro motivo è, ad esempio, se si desidera creare un elenco di caselle ed eseguire alcune operazioni su ciascuna, ma si desidera che l'elenco contenga diversi tipi di caselle. Su ogni scatola potresti fare:

myBox.close()

(supponendo che IBox abbia un metodo close ()) anche se la classe effettiva di myBox cambia a seconda della casella in cui ci si trova nell'iterazione.


54
Non c'è nulla in questa risposta che sia esclusivo delle interfacce Java . Lo stesso vale ugualmente per le classi astratte o anche per quelle concrete. Mi aspetterei una buona risposta per menzionare la capacità di implementare più interfacce e quando / perché sarebbe utile.
Rogério,

16
Come è stato selezionato come risposta? È una breve descrizione del perché il polimorfismo è utile, ma come diceva il poster sopra, mi aspetterei una spiegazione migliore di più interfacce e ancora più importante quando è appropriato usare un'interfaccia contro una classe astratta.
trevorkavanaugh,

2
Questo ha ben poco a che fare con la spiegazione delle interfacce e tutto ciò che riguarda le basi del polimorfismo
A-Developer-Has-No-Name

123

Ciò che rende utili le interfacce non è il fatto che "puoi cambiare idea e utilizzare un'implementazione diversa in un secondo momento e devi solo cambiare il luogo in cui viene creato l'oggetto". Questo è un problema.

Il vero punto è già nel nome: definiscono un'interfaccia che chiunque può implementare per usare tutto il codice che opera su quell'interfaccia. Il miglior esempio è java.util.Collectionsche fornisce tutti i tipi di metodi utili che funzionano esclusivamente su interfacce, come sort()o reverse()per List. Il punto qui è che questo codice può ora essere usato per ordinare o invertire qualsiasi classe che implementa le Listinterfacce - non solo ArrayListe LinkedList, ma anche classi che scrivi tu stesso, che possono essere implementate in un modo che le persone che hanno scritto java.util.Collectionsnon avrebbero mai immaginato.

Allo stesso modo, puoi scrivere codice che funziona su interfacce ben note o interfacce definite da te e altre persone possono usare il tuo codice senza doverti chiedere di supportare le loro classi.

Un altro uso comune delle interfacce è per i callback. Ad esempio, java.swing.table.TableCellRenderer , che consente di influenzare il modo in cui una tabella Swing visualizza i dati in una determinata colonna. Implementate quell'interfaccia, passate un'istanza a JTable, e ad un certo punto durante il rendering della tabella, il vostro codice verrà chiamato per fare le sue cose.


9
Questa è una risposta abbastanza buona, mi piace quando hai dato esempi da classi di pacchetti java ...
Owais Qureshi,

1
Mi è piaciutoyou can write code that operates on well-known interfaces, or interfaces you define
Manish Kumar l'

Aspetta un momento ... Ciò che rende utili le interfacce non è [la capacità di utilizzare qualsiasi implementazione che ti piace], ma piuttosto [la capacità di utilizzare qualsiasi implementazione che ti piace]? Citare il caso opposto è praticamente un buon punto però.
Powerslave,

4
@Powerslave: parafrasatelo di più come Ciò che rende utili le interfacce non è [la capacità di scrivere codice in cui è necessario cambiare solo una riga quando si cambia l'impementazione] ma piuttosto [la capacità di scrivere codice in cui non si specifica nemmeno un'implementazione in tutti].
Michael Borgwardt,

@MichaelBorgwardt Sembra molto meglio. :) Grazie per il chiarimento!
Powerslave,

119

Uno dei tanti usi che ho letto è dove è difficile senza interfacce con ereditarietà multipla in Java:

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

Ora, immagina un caso in cui:

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

ma,

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

Un design migliore sarebbe:

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

L'animale non ha il metodo chew () e invece viene inserito in un'interfaccia come:

interface Chewable {
void chew();
}

e chiedi alla classe Rettile di implementare questo e non gli uccelli (poiché gli uccelli non possono masticare):

class Reptile extends Animal implements Chewable { } 

e in caso di Uccelli semplicemente:

class Bird extends Animal { }

6
@CHEBURASHKA E cattiva denominazione. Se Reptile"mastica", di per sé non è "masticabile". La convenzione delle (a volte) interfacce di denominazione Qualunque cosa dovrebbe essere applicata solo dove ha perfettamente senso. Denominare l'interfaccia Predatorsarebbe più appropriato qui.
Powerslave,

6
@Powerslave È corretto imho, un rettile è "capace di masticare" / "masticabile". Un falco è un predatore ma non è ancora in grado di masticare ... solo il nitpicking ma "masticabile" può essere definito meglio nella documentazione dell'interfaccia.
Madmenyo,

Superbo .. Spiegato molto bene. Grazie..!
Gurusinghe,

1
Non capisco Tutto ciò che sembra è che le interfacce siano un buon modo per assicurarsi di implementare gli stessi metodi in ogni classe che lo implementa, (ad esempio, quindi mi impedisce di fare la stupida scelta di una classe Bird con run () e una classe Dog con runn () - sono tutti uguali). Ma prestando attenzione e facendo in modo che le mie classi abbiano gli stessi formati / strutture di metodo, non potrei ottenere questa stessa cosa? Sembra davvero che le interfacce si assicurino che il programmatore non si stia dimenticando. Inoltre, le interfacce non mi fanno risparmiare tempo; Devo ancora definire il metodo in ogni classe che lo implementa.
Alex G,

@AlexG - ho detto a uno dei tanti usi amico :) Ce ne sono altri, abbiamo appena graffiato la superficie per rispondere alla domanda in modo semplice!
Peevesy,

47

Lo scopo delle interfacce è il polimorfismo , ovvero la sostituzione del tipo . Ad esempio, dato il seguente metodo:

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

Quando si chiama il scalemetodo, è possibile fornire qualsiasi valore di tipo che implementa l' IBoxinterfaccia. In altre parole, se Rectanglee Squareentrambi implementano IBox, è possibile fornire a Rectangleo a Squareovunque IBoxci si aspetti.


8
Perché lo scopo del polimorfismo delle interfacce, se posso già raggiungerlo in Java con la sottoclasse e l'override del metodo?
eljenso,

1
È la stessa cosa, tranne per il fatto che le interfacce devono omettere qualsiasi implementazione. Le classi possono quindi implementare più di un'interfaccia.
Apocalisp

4
Ehi, non ho mai detto che Java avesse alcun tipo di integrità concettuale. La sostituzione del tipo è lo scopo di tutti i sottotipi. Java sembra avere più di un meccanismo di sottotipizzazione, nessuno dei quali è particolarmente buono.
Apocalisp

1
Non ho mai detto nulla sull'integrità concettuale. Ma andiamo avanti. Se puoi ridimensionare ogni IBox con il tuo metodo, non dovrebbe essere un'operazione dichiarata su IBox: IBox.scale (int)?
eljenso,

1
Non vorremmo accoppiare Integer a IBox, ecco perché non lo facciamo un metodo su Integer. E il numero di metodi su un'interfaccia è deciso dalla coerenza e dalla coesione dell'astrazione che esprime, non da quanto sarebbe ingombrante implementarla. Comunque, grazie per le risposte Apo.
eljenso,

33

Le interfacce consentono linguaggi tipizzati staticamente per supportare il polimorfismo. Un purista orientato agli oggetti insisterebbe sul fatto che un linguaggio dovrebbe fornire eredità, incapsulamento, modularità e polimorfismo al fine di essere un linguaggio orientato agli oggetti completo. Nelle lingue tipizzate in modo dinamico - o in quelle anatre - (come Smalltalk,) il polimorfismo è banale; tuttavia, nei linguaggi tipicamente statici (come Java o C #), il polimorfismo è tutt'altro che banale (in effetti, in superficie sembra essere in contrasto con la nozione di tipizzazione forte.)

Lasciami dimostrare:

In un linguaggio di tipo dinamico (o di tipo duck) (come Smalltalk), tutte le variabili sono riferimenti ad oggetti (niente di meno e niente di più.) Quindi, in Smalltalk, posso fare questo:

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

anAnimal := Cow new.
anAnimal makeNoise.

Quel codice:

  1. Dichiara una variabile locale chiamata anAnimal (nota che NON specifichiamo il TIPO della variabile - tutte le variabili sono riferimenti a un oggetto, né più né meno).
  2. Crea una nuova istanza della classe denominata "Pig"
  3. Assegna quella nuova istanza di Pig alla variabile anAnimal.
  4. Invia il messaggio makeNoiseal maiale.
  5. Ripete il tutto usando una mucca, ma assegnandola alla stessa variabile esatta del Maiale.

Lo stesso codice Java sarebbe simile a questo (ipotizzando che Duck e Cow siano sottoclassi di Animal:

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

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

Va tutto bene, fino a quando non introduciamo la classe Vegetable. Le verdure hanno lo stesso comportamento degli animali, ma non tutte. Ad esempio, sia animali che vegetali potrebbero essere in grado di crescere, ma chiaramente le verdure non fanno rumore e gli animali non possono essere raccolti.

In Smalltalk, possiamo scrivere questo:

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

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

Funziona perfettamente in Smalltalk perché è tipizzato come un'anatra (se cammina come un'anatra e ciondola come un'anatra - è un'anatra.) In questo caso, quando un messaggio viene inviato a un oggetto, viene eseguita una ricerca su l'elenco dei metodi del destinatario e, se viene trovato un metodo corrispondente, viene chiamato. In caso contrario, viene generata una sorta di eccezione NoSuchMethodError, ma viene eseguita al momento dell'esecuzione.

Ma in Java, un linguaggio tipicamente statico, che tipo possiamo assegnare alla nostra variabile? Il mais ha bisogno di ereditare dalla verdura, per sostenere la crescita, ma non può ereditare dall'animale, perché non fa rumore. La mucca deve ereditare dall'animale per supportare makeNoise, ma non può ereditare da Vegetable perché non dovrebbe implementare il raccolto. Sembra che abbiamo bisogno dell'ereditarietà multipla : la capacità di ereditare da più di una classe. Ma questa risulta essere una caratteristica del linguaggio piuttosto difficile a causa di tutti i casi limite che compaiono (cosa succede quando più di una superclasse parallela implementa lo stesso metodo ?, ecc.)

Arrivano le interfacce ...

Se realizziamo classi di animali e verdure, con ciascuna di esse coltivabile, possiamo dichiarare che la nostra mucca è animale e il nostro mais è vegetale. Possiamo anche dichiarare che sia animali che vegetali sono coltivabili. Questo ci permette di scrivere questo per far crescere tutto:

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

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

E ci permette di fare questo, per fare rumori di animali:

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

Il vantaggio del linguaggio tipografico è che si ottiene un polimorfismo davvero gradevole: tutto ciò che una classe deve fare per fornire un comportamento è fornire il metodo. Finché tutti giocano bene e inviano solo messaggi che corrispondono a metodi definiti, tutto va bene. Lo svantaggio è che il tipo di errore riportato di seguito non viene rilevato fino al runtime:

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

I linguaggi tipizzati staticamente forniscono una "programmazione per contratto" molto migliore perché colpiranno i due seguenti tipi di errore al momento della compilazione:

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

Quindi .... per riassumere:

  1. L'implementazione dell'interfaccia consente di specificare quali tipi di cose possono fare gli oggetti (interazione) e l'ereditarietà delle classi consente di specificare come le cose dovrebbero essere fatte (implementazione).

  2. Le interfacce ci offrono molti dei vantaggi del polimorfismo "vero", senza sacrificare il controllo del tipo di compilatore.


2
Questo è il testo della mia risposta a un'altra domanda: stackoverflow.com/questions/379282/… . Ma sono risposte correlate.
Jared,

2
Quindi, posso chiederle, in che modo un linguaggio tipizzato da anatra distingue tra Animal.water () (che, il contadino prudente diceva che subisce una perdita) e Plant.water () che usa per innaffiare le piante. L'ambiguità è il nemico. Qualsiasi quantità di verbosità necessaria per superare l'ambiguità è accettabile dall'IMO.
Bill K,

1
Sì ... ambiguità è il nome del gioco con lingue dattiloscritte. Quando si lavora in modo professionale in una lingua tipizzata da papera, non è raro vedere membri (metodi e variabili) con nomi lunghi 50-100 caratteri.
Jared,

1
Un altro grande svantaggio dei linguaggi tipizzati da anatra è l'incapacità di eseguire il refactoring programmatico basato sull'analisi statica: prova a chiedere a un'immagine Smalltalk l'elenco di tutti i chiamanti del tuo metodo printString ... otterrai l'elenco di tutti i chiamanti di TUTTI i metodi printString. ...
Jared,

... perché il chiamante di Automobile # printString non può essere programmaticamente differenziato dal chiamante di NearEarthOrbit # printString.
Jared

9

Normalmente le interfacce definiscono l'interfaccia che dovresti usare (come dice il nome ;-)). Campione


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

Ora la tua funzione fooaccetta ArrayLists, LinkedLists, ... non solo un tipo.

La cosa più importante in Java è che puoi implementare più interfacce ma puoi estendere solo UNA classe! Campione:


class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
è possibile ma

class Test extends Foo, Bar, Buz {
...
}
non è!

Il tuo codice di cui sopra potrebbe anche essere: IBox myBox = new Rectangle();. L'importante è ora che myBox contenga SOLO i metodi / campi di IBox e non gli altri (eventualmente esistenti) metodi Rectangle.


1
"Elenco" dovrebbe essere un membro dell'interfaccia?
Fai clic su Aggiorna

1
Elenco è un'interfaccia nella libreria delle raccolte java.
dal

Elenco è un'interfaccia nella libreria Java standard ( java.sun.com/javase/6/docs/api/java/util/List.html ). Lo sta solo usando per illustrare il suo punto.
Michael Myers

6

Penso che tu capisca tutto ciò che fanno le interfacce, ma non stai ancora immaginando le situazioni in cui un'interfaccia è utile.

Se stai creando un'istanza, usando e rilasciando un oggetto in un ambito ristretto (ad esempio, all'interno di una chiamata di metodo), un'interfaccia non aggiunge nulla. Come hai notato, la classe concreta è nota.

Dove le interfacce sono utili è quando un oggetto deve essere creato in un posto e restituito a un chiamante che potrebbe non interessarsi dei dettagli di implementazione. Cambiamo il tuo esempio IBox in una forma. Ora possiamo avere implementazioni di Shape come Rectangle, Circle, Triangle, ecc. Le implementazioni dei metodi getArea () e getSize () saranno completamente diverse per ogni classe concreta.

Ora puoi usare una factory con una varietà di metodi createShape (params) che restituiranno una Shape appropriata a seconda dei parametri passati. Ovviamente, la factory saprà su quale tipo di Shape viene creato, ma il chiamante non avrà per preoccuparsi se si tratta di un cerchio, o un quadrato, o così via.

Ora, immagina di avere una varietà di operazioni che devi eseguire sulle tue forme. Forse è necessario ordinarli per area, impostarli tutti su una nuova dimensione e quindi visualizzarli in un'interfaccia utente. Le forme sono tutte create dalla fabbrica e quindi possono essere passate molto facilmente alle classi Sorter, Sizer e Display. Se in futuro è necessario aggiungere una classe esagonale, non è necessario modificare altro che la fabbrica. Senza l'interfaccia, l'aggiunta di un'altra forma diventa un processo molto complicato.


6

potresti fare

Ibox myBox = new Rectangle();

in questo modo stai usando questo oggetto come Ibox e non ti interessa che sia davvero Rectangle.


Ciò significa che potremmo scrivere così ?! > Rectangle inst = new Rectangle ();
Dr.jacky,

@ Mr.Hyde Se in seguito vuoi aggiungere Squareavresti un problema .... se provi a farlo senza interfacce, non puoi garantirlo Squaree Rectangleavere gli stessi metodi ... questo può provocare un incubo quando hai una base di codice più ampia ... Ricorda, le interfacce definiscono un modello.
Kolob Canyon,

6

PERCHÉ INTERFACCIA ??????

Inizia con un cane. In particolare, un pug .

Il pug 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 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 fare dei carlini e dei 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"

Diciamo che gestisco un canile e devo tenere traccia di tutti i cani che sto ospitando. Devo 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 questo chiaramente non è ottimale. Se voglio ospitare anche alcuni barboncini , devo cambiare la mia definizione di Canile per aggiungere una serie di barboncini. 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 lascialo ai tipi specifici di cani per attuare quei comportamenti particolari. L'interfaccia dice "ecco le cose che tutti i cani possono fare" ma non dice come viene fatto ogni comportamento.

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

Quindi modifico leggermente le classi Pug e Lab per implementare i comportamenti Dog. Possiamo dire che un Pug è un cane e un laboratorio è un cane.

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 istanziare Pugs and Labs come in precedenza, ma ora ho anche un nuovo modo di farlo:

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

Questo dice che d1 non è solo un cane, è specificamente un Pug. E d2 è anche un cane, in particolare un laboratorio. Possiamo invocare i comportamenti e funzionano come prima:

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

Ecco dove tutto il lavoro extra paga. La classe Kennel diventa molto più semplice. Ho bisogno solo di un array e di un metodo addDog. Entrambi lavoreranno con qualsiasi oggetto che sia un cane; cioè 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();

Verrà visualizzata l'ultima affermazione: Spot Fido

Un'interfaccia ti dà la possibilità di specificare una serie di comportamenti condivisi da tutte le classi che implementano l'interfaccia. Di conseguenza, possiamo definire variabili e raccolte (come gli array) che non devono sapere in anticipo quale tipo di oggetto specifico terranno, solo che manterranno oggetti che implementano l'interfaccia.


@niranjan kurambhatti posso fare tutte le classi per estendere il cane ma ancora perché interfaccia ??
Jeeva

3

Un ottimo esempio di come vengono utilizzate le interfacce è nel framework Collezioni. Se scrivi una funzione che accetta a List, allora non importa se l'utente passa in a Vectoro a ArrayListo a HashListo altro. E si può passare che Lista qualsiasi funzione che richiede una Collectiono Iterableinterfacciarsi troppo.

Questo rende le funzioni Collections.sort(List list)possibili, indipendentemente da come Listè implementato.


3

Questo è il motivo per cui i pattern di fabbrica e altri modelli di creazione sono così popolari in Java. Hai ragione sul fatto che senza di essi Java non fornisce un meccanismo pronto all'uso per una facile astrazione dell'istanza. Tuttavia, ottieni astrazione ovunque dove non crei un oggetto nel tuo metodo, che dovrebbe essere la maggior parte del tuo codice.

Inoltre, in genere incoraggio le persone a non seguire il meccanismo "IRealname" per le interfacce di denominazione. Questa è una cosa Windows / COM che mette un piede nella tomba della notazione ungherese e non è davvero necessario (Java è già fortemente tipizzato, e il punto principale di avere interfacce è di averli il più largamente indistinguibili dai tipi di classe possibile).


1
Stai confondendo la tipizzazione forte con la digitazione statica.
eljenso,

3

Non dimenticare che in un secondo momento puoi prendere una classe esistente e farla implementare IBox, e sarà quindi disponibile per tutto il tuo codice compatibile con la casella.

Questo diventa un po 'più chiaro se le interfacce sono denominate -able . per esempio

public interface Saveable {
....

public interface Printable {
....

ecc. (Gli schemi di denominazione non funzionano sempre, ad esempio non sono sicuro che Boxablesia appropriato qui)


3

l'unico scopo delle interfacce è assicurarsi che la classe che implementa un'interfaccia abbia i metodi corretti in essa descritti da un'interfaccia? O c'è qualche altro uso delle interfacce?

Sto aggiornando la risposta con nuove funzionalità di interfaccia, che sono state introdotte con Java 8 versione .

Dalla pagina della documentazione di Oracle sul riepilogo dell'interfaccia :

Una dichiarazione di interfaccia può contenere

  1. firme del metodo
  2. metodi predefiniti
  3. metodi statici
  4. definizioni costanti.

Gli unici metodi che hanno implementazioni sono metodi predefiniti e statici.

Usi dell'interfaccia :

  1. Per definire un contratto
  2. Collegare le classi non correlate con ha una capacità (ad esempio l'implementazione delle classiSerializable interfaccia possono o meno avere alcuna relazione tra loro tranne implementare quell'interfaccia
  3. Fornire un'implementazione intercambiabile, ad esempio modello di strategia
  4. Metodi predefiniti consentono di aggiungere nuove funzionalità alle interfacce delle librerie e garantire la compatibilità binaria con il codice scritto per le versioni precedenti di tali interfacce
  5. Organizza i metodi di supporto nelle tue librerie con metodi statici (puoi mantenere metodi statici specifici per un'interfaccia nella stessa interfaccia anziché in una classe separata)

Alcune domande SE correlate in merito alla differenza tra classe astratta e interfaccia e casi d'uso con esempi funzionanti:

Qual è la differenza tra un'interfaccia e una classe astratta?

Come avrei dovuto spiegare la differenza tra un'interfaccia e una classe astratta?

Dai un'occhiata alla pagina della documentazione per comprendere le nuove funzionalità aggiunte in Java 8: metodi predefiniti e metodi statici .


Ho rimosso il tag java-8 poiché la domanda non faceva nulla su java-8 (e in realtà era stata posta molto tempo prima di java-8). I tag sono per domande, non per risposte.
Tagir Valeev

2

Lo scopo delle interfacce è l' astrazione o il disaccoppiamento dall'implementazione.

Se introduci un'astrazione nel tuo programma, non ti interessano le possibili implementazioni. Sei interessato a cosa può fare e non a come , e usi un interfaceper esprimerlo in Java.


Lo scopo di tutta la programmazione strutturata è l'astrazione. Perché diresti che lo scopo delle interfacce è l'astrazione, dal momento che posso ottenere esattamente la stessa cosa usando i generici e la composizione di classe?
Apocalisp

1
Se tutta la programmazione strutturata è astrazione (la tua affermazione), allora le interfacce sono astrazioni in quell'astrazione.
eljenso

1

Se si dispone di CardboardBox e HtmlBox (entrambi i quali implementano IBox), è possibile passare entrambi a qualsiasi metodo che accetta un IBox. Anche se sono entrambi molto diversi e non completamente intercambiabili, i metodi che non si preoccupano di "aprire" o "ridimensionare" possono comunque utilizzare le tue classi (forse perché si preoccupano di quanti pixel sono necessari per visualizzare qualcosa su uno schermo).


1

Interfacce in cui una fetatura è stata aggiunta a Java per consentire l'ereditarietà multipla. Gli sviluppatori di Java però hanno capito che avere l'ereditarietà multipla era una caratteristica "pericolosa", ecco perché è venuta l'idea di un'interfaccia.

l'ereditarietà multipla è pericolosa perché potresti avere una classe come la seguente:


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
}

Quale sarebbe il metodo che dovrebbe essere chiamato quando usiamo


   FunckyFigure.GetArea(); 

Tutti i problemi sono risolti con le interfacce, perché sai che puoi estendere le interfacce e che non avranno metodi di classificazione ... ovviamente il compilatore è carino e ti dice se non hai implementato un metodo, ma mi piace pensare che sia un effetto collaterale di un'idea più interessante.


È possibile che si desideri fare la differenza tra l'ereditarietà multipla dell'implementazione e l'eredità multipla dell'interfaccia nella risposta, altrimenti diventa confusa.
eljenso,

0

Ecco la mia comprensione del vantaggio dell'interfaccia. Correggimi se sbaglio. Immagina di sviluppare un sistema operativo e che altri team stiano sviluppando i driver per alcuni dispositivi. Quindi abbiamo sviluppato un'interfaccia StorageDevice. Ne abbiamo due implementazioni (FDD e HDD) fornite da altri team di sviluppatori.

Quindi abbiamo una classe OperatingSystem che può chiamare metodi di interfaccia come saveData semplicemente passando un'istanza di classe implementata l'interfaccia StorageDevice.

Il vantaggio qui è che non ci interessa l'implementazione dell'interfaccia. L'altro team farà il lavoro implementando l'interfaccia 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 stessa cosa può essere fatta con le classi astratte StorageDevice e FDD e HDD che estendono la classe StorageDevice. ma se usiamo la classe astratta non possiamo sfruttare l'ereditarietà multipla.
Vladimir Georgiev,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.