Question

Pourquoi Collection.remove (Object o) générique?

On dirait que Collection<E> pourrait avoir boolean remove(E o);

Ensuite, lorsque vous tentez accidentellement de supprimer (par exemple) Set<String> au lieu de chaque chaîne individuelle d'un Collection<String>, ce sera une erreur de compilation au lieu d'un problème de débogage ultérieurement.

Était-ce utile?

La solution

Josh Bloch et Bill Pugh font référence à ce problème dans Java Puzzlers IV: Le Menace de référence fantôme, attaque du clone et vengeance du Décalage .

Josh Bloch dit (6:41) qu'ils ont tenté de généraliser la méthode get de la carte, supprimer la méthode et une autre, mais & "cela n'a tout simplement pas fonctionné &";.

Il y a trop de programmes raisonnables qui ne pourraient pas être générés si vous autorisez uniquement le type générique de la collection en tant que type de paramètre. L’exemple donné par lui est une intersection entre un List de Number s et un Long des <=> s.

Autres conseils

remove() (dans Map ainsi que dans Collection) n'est pas générique, car vous devriez pouvoir transmettre n'importe quel type d'objet à remove(o). L'objet supprimé ne doit pas nécessairement être du même type que l'objet que vous transmettez à e; il faut seulement qu'ils soient égaux. De la spécification de (o==null ? e==null : o.equals(e)), true supprime l'objet o tel que equals() est Object. Notez que rien n'exige que List.equals() et List soient du même type. Cela découle du fait que la méthode Map<ArrayList, Something> prend en paramètre un LinkedList, pas uniquement le même type que l'objet.

Bien qu'il soit généralement vrai que de nombreuses classes ont <=> définies de sorte que leurs objets ne puissent être égaux qu'à des objets de leur propre classe, ce n'est certainement pas toujours le cas. Par exemple, la spécification de <=> indique que deux objets de liste sont égaux s'ils sont à la fois de même liste et ont le même contenu, même s'il s'agit d'implémentations différentes de <=>. Pour revenir à l'exemple de cette question, il est possible d'avoir un <=> et d'appeler <=> avec un <=> en tant qu'argument, ce qui devrait supprimer la clé qui est une liste avec le même contenu. Cela ne serait pas possible si <=> était générique et restreignait son type d'argument.

Parce que si votre paramètre de type est un caractère générique, vous ne pouvez pas utiliser une méthode de suppression générique.

Il me semble que je me suis rappelé avoir rencontré cette question avec la méthode get (Object) de Map. La méthode get dans ce cas n'est pas générique, bien qu'elle devrait raisonnablement s'attendre à recevoir un objet du même type que le premier paramètre de type. J'ai réalisé que si vous transmettez des cartes avec un caractère générique comme premier paramètre de type, il est alors impossible d'extraire un élément de la carte avec cette méthode, si cet argument est générique. Les arguments génériques ne peuvent pas vraiment être satisfaits, car le compilateur ne peut garantir que le type est correct. Je suppose que la raison pour laquelle add est générique est que vous êtes censé garantir que le type est correct avant de l'ajouter à la collection. Cependant, lors de la suppression d'un objet, si le type est incorrect, il ne correspondra à rien de toute façon. Si l'argument était un caractère générique, la méthode serait simplement inutilisable, même si vous pouvez avoir un objet que vous pouvez GARANTIR appartient à cette collection, car vous venez de recevoir une référence à la ligne précédente ....

Je ne l'ai probablement pas très bien expliqué, mais cela me semble assez logique.

En plus des autres réponses, il existe une autre raison pour laquelle la méthode doit accepter un Object, ce sont des prédicats. Considérez l'exemple suivant:

class Person {
    public String name;
    // override equals()
}
class Employee extends Person {
    public String company;
    // override equals()
}
class Developer extends Employee {
    public int yearsOfExperience;
    // override equals()
}

class Test {
    public static void main(String[] args) {
        Collection<? extends Person> people = new ArrayList<Employee>();
        // ...

        // to remove the first employee with a specific name:
        people.remove(new Person(someName1));

        // to remove the first developer that matches some criteria:
        people.remove(new Developer(someName2, someCompany, 10));

        // to remove the first employee who is either
        // a developer or an employee of someCompany:
        people.remove(new Object() {
            public boolean equals(Object employee) {
                return employee instanceof Developer
                    || ((Employee) employee).company.equals(someCompany);
        }});
    }
}

Le fait est que l'objet transmis à la méthode remove est responsable de la définition de la méthode equals. La construction de prédicats devient très simple de cette façon.

Supposons que l'un d'entre eux possède une collection de Cat et certaines références d'objet de types Animal, SiameseCat, Dog et <=>. Demander à la collection si elle contient ou non l'objet référencé par la référence <=> ou <=> semble raisonnable. Demander si elle contient l’objet mentionné par la <=> référence peut sembler louche, mais cela reste parfaitement raisonnable. Après tout, l’objet en question peut être <=> et apparaître dans la collection.

De plus, même s'il se trouve que l'objet est autre chose qu'un <=>, il n'est pas difficile de dire s'il apparaît ou non dans la collection - répondez simplement à & "non, ce n'est pas &"; . Un & "Lookup-style &"; Une collection d'un certain type doit pouvoir accepter de manière significative la référence de n'importe quel supertype et déterminer si l'objet existe dans la collection. Si la référence d'objet transmise est d'un type non associé, la collection ne pourrait pas la contenir. La requête n'a donc aucun sens (elle répondra toujours à & Quot; no & Quot;). Néanmoins, comme il n’existe aucun moyen de limiter les paramètres aux sous-types ou aux supertypes, il est très pratique d’accepter tout type et de répondre à & «Non &»; pour tous les objets dont le type est sans rapport avec celui de la collection.

J'ai toujours pensé que c'était parce que remove () n'avait aucune raison de se soucier du type d'objet que vous lui donniez. Quoi qu'il en soit, il est assez facile de vérifier si cet objet est l'un de ceux que contient la Collection, car il peut appeler equals () à tout moment. Il est nécessaire de vérifier le type sur add () pour s’assurer qu’il ne contient que des objets de ce type.

La suppression n'est pas une méthode générique, de sorte que le code existant utilisant une collection non générique sera toujours compilé et aura toujours le même comportement.

Voir http://www.ibm.com/developerworks/ java / library / j-jtp01255.html pour plus de détails.

Edition: un commentateur demande pourquoi la méthode add est générique. [... supprimé mon explication ...] Le deuxième intervenant a répondu à la question de firebird84 beaucoup mieux que moi.

Une autre raison est due aux interfaces. Voici un exemple pour le montrer:

public interface A {}

public interface B {}

public class MyClass implements A, B {}

public static void main(String[] args) {
   Collection<A> collection = new ArrayList<>();
   MyClass item = new MyClass();
   collection.add(item);  // works fine
   B b = item; // valid
   collection.remove(b); /* It works because the remove method accepts an Object. If it was generic, this would not work */
}

Parce que cela briserait le code existant (antérieur à Java5). par exemple,

Set stringSet = new HashSet();
// do some stuff...
Object o = "foobar";
stringSet.remove(o);

Maintenant, vous pourriez dire que le code ci-dessus est faux, mais supposons que o provenait d'un ensemble d'objets hétérogène (c'est-à-dire qu'il contenait des chaînes, un nombre, des objets, etc.). Vous voulez supprimer toutes les correspondances, ce qui était légal, car remove ignorerait simplement les non-chaînes car elles n'étaient pas égales. Mais si vous le supprimez (String o), cela ne fonctionne plus.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top