PMD signalerait une violation pour:
ArrayList<Object> list = new ArrayList<Object>();
La violation était "Évitez d'utiliser des types d'implémentation comme 'ArrayList'; utilisez plutôt l'interface".
La ligne suivante corrigerait la violation:
List<Object> list = new ArrayList<Object>();
Pourquoi utiliser ce dernier avec List
au lieu de ArrayList
?
L'utilisation d'interfaces sur des types concrets est la clé d'une bonne encapsulation et d'un couplage lâche de votre code.
C'est même une bonne idée de suivre cette pratique lors de l'écriture de vos propres API. Si vous le faites, vous découvrirez plus tard qu'il est plus facile d'ajouter des tests unitaires à votre code (en utilisant des techniques de simulation) et de changer l'implémentation sous-jacente si nécessaire à l'avenir.
Voici un bon article sur le sujet.
J'espère que ça aide!
Ceci est préférable car vous dissociez votre code de l'implémentation de la liste. L'utilisation de l'interface vous permet de modifier facilement l'implémentation, ArrayList dans ce cas, en une autre implémentation de liste sans modifier le reste du code tant qu'il n'utilise que les méthodes définies dans List.
En général, je suis d'accord que le découplage de l'interface de l'implémentation est une bonne chose et facilitera la maintenance de votre code.
Il y a cependant des exceptions que vous devez considérer. L'accès aux objets via des interfaces ajoute une couche supplémentaire d'indirection qui rendra votre code plus lent.
Par intérêt, j'ai mené une expérience qui a généré dix milliards d'accès séquentiels à une liste de tableaux de 1 million de longueur. Sur mon MacBook 2,4 GHz, l'accès à ArrayList via une interface List prenait en moyenne 2,10 secondes, lors de sa déclaration de type ArrayList, cela prenait en moyenne 1,67 seconde.
Si vous travaillez avec de grandes listes, au fond d'une boucle interne ou d'une fonction fréquemment appelée, c'est quelque chose à considérer.
ArrayList et LinkedList sont deux implémentations d'une liste, qui est une collection ordonnée d'éléments. Logiquement, peu importe que vous utilisiez une ArrayList ou une LinkedList, vous ne devez donc pas contraindre le type à l'être.
Cela contraste avec, disons, Collection et List, qui sont des choses différentes (List implique le tri, Collection pas).
Pourquoi ce dernier avec List devrait-il être utilisé à la place d'ArrayList?
C'est une bonne pratique: Programmez l'interface plutôt que l'implémentation
En remplaçant ArrayList
par List
, vous pouvez changer l'implémentation de List
à l'avenir comme ci-dessous en fonction de votre cas d'utilisation.
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).*/
OR
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.*/
OR
List<Object> list = new Stack<Object>();
/* The Stack class represents a last-in-first-out (LIFO) stack of objects.*/
OR
une autre implémentation spécifique de List
.
L'interface List
définit le contrat et l'implémentation spécifique de List
peut être modifiée. De cette façon, l'interface et l'implémentation sont faiblement couplées.
Question SE associée:
Les propriétés de vos classes/interfaces doivent être exposées via des interfaces car cela donne à vos classes un contrat de comportement à utiliser, quelle que soit l'implémentation.
Toutefois...
Dans les déclarations de variables locales, cela n'a guère de sens:
public void someMethod() {
List theList = new ArrayList();
//do stuff with the list
}
S'il s'agit d'une variable locale, utilisez simplement le type. Il est toujours implicitement transposable vers son interface appropriée, et vos méthodes devraient, espérons-le, accepter les types d'interface pour ses arguments, mais pour les variables locales, il est tout à fait logique d'utiliser le type d'implémentation comme conteneur, juste au cas où vous en auriez besoin. fonctionnalité spécifique.
Même pour les variables locales, l'utilisation de l'interface sur la classe concrète aide. Vous pouvez finir par appeler une méthode qui est en dehors de l'interface, puis il est difficile de modifier l'implémentation de la liste si nécessaire. En outre, il est préférable d'utiliser la classe ou l'interface la moins spécifique dans une déclaration. Si l'ordre des éléments n'a pas d'importance, utilisez une collection au lieu d'une liste. Cela donne à votre code une flexibilité maximale.
En général, pour votre ligne de code, cela n'a aucun sens de s'embêter avec les interfaces. Mais, si nous parlons d'API, il y a une très bonne raison. J'ai une petite classe
class Counter {
static int sizeOf(List<?> items) {
return items.size();
}
}
Dans ce cas, l'utilisation de l'interface est requise. Parce que je veux compter la taille de chaque possible implémentation, y compris ma propre coutume. class MyList extends AbstractList<String>...
.