Ne déclarez pas de variables dans List en Java

Ne déclarez pas de variables dans List en Java

//mauvais exemple
List<String> list = new ArrayList<>();

//Bon exemple
ArrayList<String> list = new ArrayList<>();

Jusqu'à présent, il a été dit que les variables devaient être déclarées dans le type List, qui est une interface, et en fait j'ai également déclaré un grand nombre de variables dans le type List. Mais j'en suis venu à penser que c'était une erreur. La raison est expliquée ci-dessous.

La raison pour laquelle il a été dit qu'il devrait être déclaré dans la liste

L'interface est la définition de la spécification. Il promet que "la classe qui implémente cette interface aura les méthodes écrites dans cette interface". En utilisant la classe d'implémentation via l'interface, le côté utilisateur n'a pas besoin d'être conscient de l'implémentation et un couplage lâche est réalisé.

Pour expliquer cela concrètement avec une liste Java, déclarer List <String> list = new ArrayList <> (); élimine la dépendance à la classe ArrayList. Par conséquent, même si vous remplacez l'ArrayList par une LinkedList existante ou par votre propre Saikyo Norisu, cela fonctionnera sans aucun problème. De nouvelles classes de liste peuvent être ajoutées à l'API à l'avenir, mais elles peuvent également être facilement remplacées.

L'idée prédominante actuelle est d'obtenir un couplage lâche en gérant les classes d'implémentation via l'interface List, ce qui rend le programme hautement extensible et résistant au changement.

Essayez en fait

Essayons-le.

sample

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return new ArrayList<>();
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}

C'est un échantillon qui fait juste une liste, met des nombres et l'affiche. Vous pouvez également vérifier le nom de la classe dans la liste.

Naturellement, le résultat sera comme ça.

class java.util.ArrayList
1
2
3

Remplaçons maintenant la classe List.

■CopyOnWriteArrayList

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return new CopyOnWriteArrayList<>();
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.concurrent.CopyOnWriteArrayList
1
2
3

■LinkedList

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return new LinkedList<>();
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.LinkedList
1
2
3

■ Classe de liste personnalisée

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return new SaikyoList<>();
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}

/**
 *Quand j'y pense
 *L'implémentation d'une liste est fastidieuse, alors étendez une ArrayList.
 */
public class SaikyoList<E> extends ArrayList<E>
{
	@Override
	public boolean add(E e)
	{
		//Comme c'est une bonne journée, ajoutez deux fois plus que la liste normale
		super.add(e);
		super.add(e);
		return true;
	}
}
class test.SaikyoList
1
1
2
2
3
3

■Collections.emptyList();

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return Collections.emptyList();
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.Collections$EmptyList
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.AbstractList.add(AbstractList.java:153)

Oh?

■Collections.singletonList(1);

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return Collections.singletonList(1);
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.Collections$SingletonList
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.AbstractList.add(AbstractList.java:153)

■Arrays.asList();

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return Arrays.asList();
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.Arrays$ArrayList
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.AbstractList.add(AbstractList.java:153)

■List.of();

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return List.of();
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.ImmutableCollections$List0
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:70)

■List.of(1);

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return List.of(1);
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.ImmutableCollections$List1
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:70)

■List.of(1, 2);

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return List.of(1, 2);
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.ImmutableCollections$List2
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:70)

■List.of(1, 2, 3);

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return List.of(1, 2, 3);
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.ImmutableCollections$ListN
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:70)

■Collections.unmodifiableList(new ArrayList<>());

public static void main(String[] args)
{
	List<Integer> list = getList();
	listTest(list);
}

public static List<Integer> getList()
{
	return Collections.unmodifiableList(new ArrayList<>());
}
public static void listTest(List<Integer> list)
{
	System.out.println(list.getClass());
	
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.forEach(System.out::println);
}
class java.util.Collections$UnmodifiableRandomAccessList
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1056)

UnsupportedOperationException!

UnsupportedOperationException!!

UnsupportedOperationException!!!

Je viens de dire que j'implémenterais add ...

C'était un mensonge s'il y avait Sman

Comment est-ce arrivé

L'interface List définit ajouter, supprimer, définir, etc., et il est clair que nous avons une collection de variables à l'esprit. Cependant, les listes avec un nombre fixe d'éléments (java.util.Arrays \ $ ArrayList) et les listes immuables (java.util.Collections \ $ UnmodifiableList, etc.) implémentent également l'interface List et l'une des méthodes pour réaliser l'immuabilité. Cela prend la forme de lancer simplement une "UnsupportedOperationException" pour la pièce.

En d'autres termes, Java List a des classes d'implémentation immuables et variables même s'il définit des méthodes pour changer la liste, donc les méthodes pour changer ces listes deviennent des méthodes qui ne peuvent pas être utilisées sans connaître la classe d'implémentation. C'est fermé. Loin d'être faiblement couplé, cela peut ne pas fonctionner si la classe d'implémentation est remplacée, donc même le couplage ne se fait pas correctement, c'est un faux couplage.

Ceci est clairement montré dans la méthode "Collectors.toList" utilisée comme argument de stream.collect. L'implémentation actuelle renvoie une ArrayList, mais la documentation dit:

Il n'y a aucune garantie du type, de la variabilité, de la sérialisabilité ou de la sécurité des threads de la liste renvoyée. https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Collectors.html#toList--

À l'origine, je pense que les collections immuables auraient dû être traitées comme itérables. Il faut dire que c'est une erreur de conception d'implémenter l'interface de liste car nous voulons traiter les choses qui n'ont pas la fonction comme une liste ensemble comme une liste.

~~ Après tout, Java est de la merde ... ~~

De plus, il est également ennuyeux de savoir au moment de la compilation sous la forme d'exceptions d'exécution.

Qu'est-ce qui devrait être fait

Comme mentionné ci-dessus, l'interface List et sa classe d'implémentation ont une faille fatale. Par conséquent, le but de cet article est de montrer que les méthodes add et autres sont valides en déclarant des variables comme type ArrayList au lieu de type List. En fait, une liste si immuable que List <Integer> list = new ArrayList <> (Arrays.asList (1,2,3)); est reconnue comme un idiome n'est pas facile à utiliser, alors convertissez-la en une variable ArrayList et utilisez-la. Vous le faites. (Je pense que cela dépend de l'application ...)

Si vous le déclarez dans ArrayList comme ceci, tous les exemples de code de "Try it" pourraient détecter le problème avant l'exécution comme une erreur de compilation. Cependant, LinkedList etc. qui n'ont aucun problème causera également une erreur de compilation, mais je pense que beaucoup de gens apprécieraient s'ils faisaient une erreur au moment de la compilation plutôt que de laisser une exception d'exécution.

Cependant, cela ne semble pas être bien reconnu comme un problème. Probablement pas un gros problème car les programmeurs avisés utilisent List avec précaution, mais ce n'est pas grave si vous l'utilisez avec précaution! Si vous pouvez le faire, vous n'avez pas besoin d'exceptions d'inspection ou de sécurité nulle. Personnellement, je pense qu'une liste immuable qui lève une exception uniquement lorsqu'une méthode spécifique est appelée en fonction de la classe d'implémentation est plus effrayante que null, ce qui peut être facilement déterminé par list! = Null.

Cependant, compte tenu de la situation actuelle où le problème n'est pas reconnu comme tel,

ArrayList<String> list = new ArrayList<>();
Iterable<Integer> iterable = getList();
ArrayList<Hoge> hogeList = new ArrayList<>(getList());

Il est difficile de dire qu'une telle écriture entraînera un code étrange. [^ 1]

Effective Java recommande également de «renvoyer un tableau ou une collection vide au lieu de null», vous avez donc peut-être écrit une méthode comme celle-ci.

public List<String> readFile(String path)
{
	File file = new File(path);
	if(!file.exists())
	{
		return Collections.emptyList();
	}

	List<String> list = new ArrayList<>();
	
	//Processus de lecture de fichier
	
	return list;
}

Cette méthode peut retourner une "liste immuable vide, une liste mutable vide, une liste mutable avec des éléments". N'est-ce pas effrayant?

Résumé

La réalisation du couplage lâche lui-même est bonne. Cependant, Java's List est mal conçu, donc même si vous utilisez l'interface List, il ne sera pas faiblement couplé, et il y a un risque d'exceptions d'exécution.

L'interface est une promesse d'avoir des méthodes définies. N'écrivez pas une classe d'implémentation qui brise les promesses.

Implémentez ...! Je vais le mettre en œuvre ... Cette fois encore la méthode de mise en œuvre Non spécifié </ font>

S'il vous plaît vous aussi les gars Je veux que vous vous souveniez de

En d'autres termes ... Si on en a envie ajouter est Vous pouvez simplement lancer une exception UnsupportedOperationException Ce sera possible ...! </ font>

[^ 1]: Au fait, le type Iterable peut obtenir le flux avec StreamSupport.stream (iterable.spliterator (), false);. Cependant, même si list.stream () est gênant, l'écrire à chaque fois est ...

Recommended Posts