2008-09-29 7 views
66

PMD würde für einen Verstoß melden:Warum sollte die Schnittstelle für eine Java-Klasse bevorzugt werden?

ArrayList<Object> list = new ArrayList<Object>(); 

Die Verletzung war „Vermeiden Sie Implementierungstypen wie‚Arraylist‘verwendet; die Schnittstelle verwenden, anstatt“.

Die folgende Zeile würde die Verletzung beheben:

List<Object> list = new ArrayList<Object>(); 

Warum statt werden sollte dieser ArrayList mit List verwendet?

+0

Siehe auch http: // Stackoverflow.com/questions/383947/what-does-it-mean-zu-Programm-zu-einem-Interface – Raedwald

Antwort

73

Die Verwendung von Schnittstellen über Betontypen ist der Schlüssel für eine gute Kapselung und für die lose Kopplung Ihres Codes.

Es ist sogar eine gute Idee, diese Vorgehensweise beim Schreiben eigener APIs zu befolgen. Wenn Sie dies tun, werden Sie später feststellen, dass es einfacher ist, Unit-Tests zu Ihrem Code hinzuzufügen (mithilfe von Mocking-Techniken) und die zugrunde liegende Implementierung bei Bedarf zu ändern.

Hier ist eine good article zum Thema.

Hoffe es hilft!

27

Dies ist bevorzugt, da Sie Ihren Code von der Implementierung der Liste entkoppeln. Mithilfe der Schnittstelle können Sie die Implementierung, ArrayList in diesem Fall, problemlos in eine andere Listenimplementierung ändern, ohne den restlichen Code zu ändern, solange nur die in List definierten Methoden verwendet werden.

5

ArrayList und LinkedList sind zwei Implementierungen einer Liste, bei der es sich um eine geordnete Sammlung von Elementen handelt. Logisch ist es egal, ob Sie eine ArrayList oder eine LinkedList verwenden, also sollten Sie den Typ nicht darauf beschränken.

Dies steht im Gegensatz zu sagen, Sammlung und Liste, die verschiedene Dinge sind (Liste impliziert Sortierung, Sammlung nicht).

0

Im Allgemeinen macht es für Ihre Codezeile keinen Sinn, sich mit Interfaces zu befassen. Aber wenn wir über APIs sprechen, gibt es einen wirklich guten Grund. Ich habe kleine Klasse

class Counter { 
    static int sizeOf(List<?> items) { 
     return items.size(); 
    } 
} 

In diesem Fall ist die Verwendung der Schnittstelle erforderlich. Weil ich Größe von jede mögliche Implementierung einschließlich meiner eigenen Gewohnheit zählen möchte. class MyList extends AbstractList<String>....

+0

Wenn Sie zu Implementierungen wie ArrayList codieren, gibt es jede Möglichkeit, dass Sie Methoden der Implementierung verwenden können, selbst wenn Sie dasselbe tun könnten Sache mit Schnittstellenmethoden. Dies würde Ihren Code an die Implementierung binden, wodurch es später schwieriger wird, zwischen den Implementierungen zu wechseln. – Champ

1

Eigenschaften Ihrer Klassen/Schnittstellen sollten über Schnittstellen verfügbar gemacht werden, da sie Ihren Klassen einen Verhaltensvertrag geben, unabhängig von der Implementierung.

jedoch ...

In lokalen Variablendeklarationen, macht es wenig Sinn, dies zu tun:

public void someMethod() { 
List theList = new ArrayList(); 
//do stuff with the list 
} 

Wenn ich eine lokale Variable, sondern nur die Art verwenden. Es ist immer noch implizit zu der entsprechenden Schnittstelle upcastable, und Ihre Methoden sollten hoffentlich die Schnittstellentypen für ihre Argumente akzeptieren, aber für lokale Variablen macht es durchaus Sinn, den Implementierungstyp als Container zu verwenden, nur für den Fall, dass Sie die Implementierung benötigen. spezifische Funktionalität.

10

Im Allgemeinen stimme ich zu, dass die Entkopplung von der Implementierung eine gute Sache ist und Ihren Code einfacher zu pflegen macht.

Es gibt jedoch Ausnahmen, die Sie berücksichtigen müssen. Durch den Zugriff auf Objekte über Schnittstellen wird eine zusätzliche Indirektionsebene hinzugefügt, die den Code langsamer macht.

Für Interesse führte ich ein Experiment, das zehn Milliarden sequentielle Zugriffe auf eine 1 Million Länge ArrayList erzeugte. Auf meinem 2.4Ghz MacBook dauerte der Zugriff auf die ArrayList über eine List-Schnittstelle im Durchschnitt 2.10 Sekunden, als es vom Typ ArrayList deklariert wurde, dauerte es durchschnittlich 1.67 Sekunden.

Wenn Sie mit großen Listen arbeiten, tief in einer inneren Schleife oder häufig als Funktion bezeichnet, dann ist dies etwas zu beachten.

+0

@Owen: +5 Insightful re: Leistungsunterschied ... Sehr unerwartet –

+2

Diese Antwort zeigt jedoch, dass der Overhead sehr klein sein kann: http://StackOverflow.com/Questions/890687/overhead-of-implementing-an-interface/891096 # 891096 – Raedwald

+1

Wow! 0,5 Sekunden für 10 Milliarden Zugriffe, d.h. 1 Schnittstellenzugriff ist eine halbe Nanosekunde langsamer als ein Klassenzugriff! Dies ist natürlich ein Grund, niemals Schnittstellen zu verwenden. – Ingo

1

Auch für lokale Variablen hilft die Verwendung der Schnittstelle über die konkrete Klasse. Möglicherweise rufen Sie am Ende eine Methode auf, die sich außerhalb der Schnittstelle befindet, und dann ist es schwierig, die Implementierung der Liste bei Bedarf zu ändern. Außerdem ist es am besten, die am wenigsten spezifische Klasse oder Schnittstelle in einer Deklaration zu verwenden. Wenn die Reihenfolge der Elemente keine Rolle spielt, verwenden Sie eine Sammlung anstelle einer Liste. Das gibt Ihrem Code die maximale Flexibilität.

0

Spring-Framework gilt das Konzept der Schnittstellen wirklich schön - http://www.springframework.org/

Frühling liefert die Umsetzung auf eine konkrete Klasse über Konfigurationsdatei und so die konkrete Klasse braucht nicht etwas über die Umsetzung zu kennen.

Studie von Spring veranschaulicht die Vorteile von Interface-basierter Programmierung in Java.

1

Warum sollte letzteres mit List anstelle von ArrayList verwendet werden?

Es ist eine gute Praxis: Programm statt Implementierung Schnittstelle

Durch ArrayList mit List ersetzen, können Sie List Implementierung in Zukunft ändern, da auf Ihrem Geschäftsanwendungsfall unten abhängig.

List<Object> list = new LinkedList<Object>(); 
/* Doubly-linked list implementation of the List and Deque interfaces. 
Implements all optional list operations, and permits all elements (including null).*/ 

ODER

List<Object> list = new CopyOnWriteArrayList<Object>(); 
/* A thread-safe variant of ArrayList in which all mutative operations 
(add, set, and so on) are implemented by making a fresh copy of the underlying array.*/ 

ODER

List<Object> list = new Stack<Object>(); 

/* The Stack class represents a last-in-first-out (LIFO) stack of objects.*/ 

ODER

einige andere List spezifische Implementierung.

List Schnittstelle definiert Vertrag und spezifische Implementierung von List kann geändert werden. Auf diese Weise sind Schnittstelle und Implementierung lose gekoppelt.

Verwandte SE Frage:

What does it mean to "program to an interface"?