API Java collections: pourquoi les classes non modifiables[List|Set|Map] ne sont-elles pas visibles publiquement?


Collections.unmodifiableList(...) renvoie une nouvelle instance d'une classe interne statique UnmodifiableList. D'autres classes de collections non modifiables sont construites de la même manière.

Si ces classes étaient publiques, on avait deux avantages:

  • possibilité d'indiquer une valeur de retour plus spécifique (telle que UnmodifiableList), afin qu'un utilisateur de l'API ne vienne pas à l'idée de modifier cette collection;
  • capacité à vérifier lors de l'exécution si un List est instanceof UnmodifiableList.

Donc, y avait-il des avantages pas pour rendre ces cours publics?

EDIT : Aucun argument définitivement convaincant n'a été présenté, donc je choisis la réponse la plus upvotée.

Author: java.is.for.desktop, 2010-11-28

7 answers

Je pense que les deux avantages sont là mais ne sont pas si utiles. Les principaux problèmes restent les mêmes: UnmodifiableList est toujours une Liste et donc tous les setters sont disponibles et les collections sous-jacentes sont toujours modifiables. Rendre la classe publique non modifiable ajouterait à l'illusion d'être non modifiable.

La meilleure façon serait que le compilateur aide, mais pour cela les hiérarchies de classe de collection devraient beaucoup changer. Par exemple, l'API de collection de Scala est bien plus avancée à cet égard.

Un inconvénient serait l'introduction d'au moins trois classes / interfaces supplémentaires dans l'API. Parce qu'ils ne sont pas si utiles, je pense que les laisser hors de l'API est un bon choix.

 4
Author: Christoph Dietze, 2010-11-28 17:58:54

Personnellement, je suis entièrement d'accord avec vous. Au cœur du problème est le fait que les génériques de Java ne sont pas covariants, ce qui, à son tour, est dû au fait que les collections de Java sont mutables.

, Il n'est pas possible pour Java du type de système de codifier un type qui semble avoir mutateurs est réalité immuable. Imaginez si nous devions commencer à concevoir une solution:

interface Immutable //marker for immutability

interface ImmutableMap<K, V> extends Map<K, V>, Immutable

, Mais alors ImmutableMap est une sous-classe de Map, et donc Map est assignable à partir de ImmutableMap de sorte que toute méthode qui renvoie une telle carte immuable:

public ImmutableMap<K, V> foo();

Peut être affecté à une carte et peut donc être muté au moment de la compilation:

Map<K, V> m = foo();
m.put(k, v); //oh dear

Donc, vous pouvez voir que l'ajout de ce type ne nous a pas empêché de faire quelque chose de mal. Je pense que pour cette raison, on a jugé qu'il n'avait pas assez à offrir.


Un langage comme scala a des annotations de variance de site de déclaration. Autrement dit, vous pouvez spécifier un type comme étant covariant (et par conséquent immuable) comme l'est la carte de Scala (en fait, elle est covariante dans son paramètre V). Par conséquent, votre API peut déclarer si son type de retour est mutable ou immuable.

Par ailleurs, Scala vous permet de déclarer les types d'intersection afin que vous n'ayez même pas besoin de créer l'interface ImmutableXYZ en tant qu'entité distincte, vous pouvez spécifier une méthode pour retourner:

def foo : XYZ with Immutable

Mais alors scala a un système de type approprié, alors que Java ne le fait pas

 6
Author: oxbow_lakes, 2010-11-28 20:39:06

S'il est important pour vous de vérifier si la liste a été créée avec des Collections.unmodifiableList alors vous pouvez créer une instance et demander la classe. Maintenant, vous vous pouvez comparer cette classe avec la classe de toute liste.

private static Class UNMODIFIABLE_LIST_CLASS = 
    Collections.unmodifiableList( new ArrayList() ).getClass();
...
if( UNMODIFIABLE_LIST_CLASS == listToTest.getClass() ){
    ...
} 
 3
Author: Horcrux7, 2010-11-28 19:29:13

La réponse au pourquoi est assez simple: à l'époque, en 1998, le design efficace était un peu flanqué. Les gens y pensaient, ce n'était apparemment pas une priorité. Mais il n'y avait pas de vraie réflexion profonde à ce sujet.

Si vous souhaitez utiliser un tel mécanisme, utilisez ImmutableList/Set/Map/de Guava...

Ils sont explicitement immuables et une bonne pratique lors de l'utilisation de cette bibliothèque n'est pas de renvoyer une liste par exemple mais une ImmutableList. Donc, vous saurez qu'une liste / Set / Map/... être immuable.

Exemple:

private final ImmutableList constants = ...;
public final ImmutableList<String> getConstants() {
  return constants;
}

A propos de la conception elle-même de UnmodifiableXxx, on aurait pu faire ce qui suit:

public static final class UnmodifiableXxx implements Xxx { // don't allow extend
  // static if inside Collections
  UnmodifiableXxx (Xxx backend) { // don't allow direct instanciation
    ...
  }
  ...
}
 3
Author: Olivier Grégoire, 2011-01-03 10:14:05
  • possibilité d'indiquer une valeur de retour plus spécifique (telle que UnmodifiableList), afin qu'un utilisateur de l'API ne vienne pas à l'idée de modifier cette collection;

Dans une API appropriée, cela devrait déjà être documenté dans le javadoc de la méthode renvoyant la liste non modifiable.

  • capacité à vérifier lors de l'exécution si un List est instanceof UnmodifiableList.

Un tel besoin indique que le problème réel se trouve ailleurs. C'est une faille dans la conception du code. Demandez-vous, avez-vous déjà avait la nécessité de vérifier si un List est une instance de ArrayList ou LinkedList? Que ce soit un ArrayList, LinkedList ou UnmodifiableList est clairement une décision qui doit être prise pendant le temps d'écriture du code, pas pendant l'exécution du code. Si vous rencontrez des problèmes parce que vous essayez de modifier un UnmodifiableList (pour lequel le développeur de l'API peut avoir de très bonnes raisons qui devraient être déjà documentées), alors c'est plutôt votre propre faute, pas un défaut d'exécution.

Tous avec tous les, il ne fait aucun sens. Le Collections#unmodifiableXXX(), synchronizedXXX() et checkedXXX() ne représentent en aucune façon des implémentations concrètes. Ils sont tous juste décorateurs qui peut être appliqué indépendamment de la mise en œuvre concrète sous-jacente.

 1
Author: BalusC, 2010-11-28 20:26:05

Supposons UnmodifiableList était une classe publique. Je soupçonne que cela bercerait les programmeurs dans un faux sentiment de sécurité. Rappelez-vous, UnmodifiableList est un afficher de modifiable List. Cela signifie que le contenu d'un UnmodifiableList peut toujours changer via les modifications apportées à son List sous-jacent. Un programmeur naïf peut ne pas comprendre cette nuance et peut s'attendre à ce que les instances de UnmodifiableList soient immuables.

 0
Author: Adam Paynter, 2010-11-28 12:22:58

Je pense que la réponse est parce que le formulaire de méthode connaît correctement les génériques utilisés et ne nécessite aucune programmation supplémentaire pour transmettre ces informations, tandis que le formulaire de classe nécessiterait plus de déconner. La forme de méthode pour unmodifiableMap a deux arguments génériques flottants, qu'elle mappe à la fois aux arguments génériques du type de retour et de l'argument passé.

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
    return new UnmodifiableMap<K,V>(m);
}
 0
Author: Chris Dennett, 2010-11-28 18:07:58