J'ai une application où j'utilise des tableaux et des listes primitifs pour une classe appelée Item. Ceux-ci sont utilisés de manière interchangeable pour des raisons héritées (je souhaite également que ce ne soit qu'un type, mais c'est ainsi).
Maintenant, je dois ajouter une nouvelle méthode comme celle-ci qui fonctionne via une boucle pour chaque:
public void something(Item... items) {
for (Item i : items) {
doStuff();
}
}
public void something(List<Item> items) {
for (Item i : items) {
doStuff();
}
}
En d'autres termes, exactement la même méthode deux fois pour les tableaux et les listes primitifs. Existe-t-il un moyen de refactoriser correctement cela en une seule méthode?
Vous ne peut pas ne devrait pas (*) faire cela dans une seule méthode. Item[]
Et List<Item>
Sont des types indépendants.
Vous devez faire appel à l'une des surcharges: something(Item... items)
appelle something(List<Item>)
ou something(List<Item>)
appelle something(Item... items)
.
Parmi les deux options, il est préférable que la surcharge du tableau appelle la surcharge de liste:
public void something(Item... items) {
something(Arrays.asList(item));
}
C'est bon marché, car il ne copie pas le tableau, mais l'enveloppe plutôt: la création de List
est O(1)
.
Si vous deviez invoquer la surcharge du tableau à partir de la surcharge de la liste:
public void something(List<Item> items) {
something(items.toArray(new Item[0]));
}
Ce serait plus cher, puisque l'appel toArray
doit créer et remplir un tableau: c'est une opération O(n)
, où n
est la taille de la liste. Cependant, il a le léger avantage que something
ne serait pas en mesure de remplacer le contenu de List
, car les mises à jour du tableau sont simplement ignorées après l'exécution.
(*) Vous pouvez, mais ce serait vraiment grossier et non sûr pour le type, car vous devez accepter un paramètre Object
, car il n'y a pas d'autre super type commun de List<Item>
et Item[]
; et vous finiriez toujours par devoir répéter les boucles pour les deux types; et vous devez gérer la possibilité de passer un type complètement indépendant (au moment de l'exécution):
public void something(Object obj) {
if (obj instanceof List) {
for (Object element : (List<?>) obj) {
Item item = (Item) element; // Potential ClassCastException.
doStuff();
}
} else if (obj instanceof Item[]) {
for (Item item : (Item[]) obj) {
doStuff();
}
} else {
throw new IllegalArgumentException();
}
}
Quel bordel. Merci au fabricant pour les surcharges.
Si vous utilisez Java 8, vous pouvez aussi simplement appeler forEach
ou map
sur votre - Stream
et vous avez terminé, par exemple.
yourStream.forEach(doStuff());
où doStuff()
est le consommateur traitant de la chaîne ou utilisez yourStream.forEach(s -> doStuff())
si vous ne voulez pas gérer la chaîne et juste do stuff
.
Vous pouvez obtenir un flux comme suit:
Stream.of(yourArray) // or Arrays.stream(yourArray)
.forEach(doStuff());
et pour votre liste:
list.stream()
.forEach(doStuff());
Le principal avantage de l'utilisation des flux est probablement la lisibilité. Il peut perdre en termes de performances et peut également perdre si vous ne voulez pas appeler Stream.of/Arrays.stream
Ou Collection.stream()
juste pour obtenir le flux.
Si vous voulez vraiment garder la méthode something(...)
(être capable de gérer les deux: les varargs et la liste) vous avez toujours besoin d'une méthode surchargée ou utilisez la proposition d'Andy Turner avec le Object
- méthode-paramètre.
Vous pouvez implémenter une seule méthode, dans ce cas, la seconde car elle a une liste en paramètre. Au lieu de la première méthode, vous pouvez convertir le tableau dans une liste avec Arrays.asList(items)
, puis vous pouvez appeler la première méthode. Donc, au final, vous n'aurez qu'une seule méthode (qui a une liste en paramètre).
De plus, si la liste des éléments contient peu d'éléments, vous pouvez utiliser les expressions lambda de Java 8:
items.foreach(item -> doStuff(item));
Ainsi, vous n'aurez pas de méthode qui ne contienne qu'une seule boucle et le code sera plus facile à lire.
Vous devriez y parvenir en passant List après l'avoir converti en tableau.
Conservez cela comme votre méthode unique,
public void something(Item... items) {
for (Item i : items) {
doStuff();
}
}
et quand vous voulez passer un List<Item>
alors passez comme ça,
something(listItem.toArray(new Item[listItem.size()]))