Domanda

Perché non è Collezione.remove(Object o) generico?

Sembra Collection<E> potrebbe avere boolean remove(E o);

Poi, accidentalmente quando si tenta di rimuovere (per esempio) Set<String> invece di ogni singola Stringa da un Collection<String>, sarebbe un errore tempo di compilazione, invece, di un problema di debug più tardi.

È stato utile?

Soluzione

Josh Bloch e Bill Pugh fanno riferimento a questo problema in Java Puzzlers IV: The Phantom Reference Minace, Attack of the Clone e Revenge of The Maiusc .

Josh Bloch dice (6:41) che hanno tentato di generare il metodo get di Map, rimuovi il metodo e alcuni altri, ma " semplicemente non ha funzionato " ;.

Esistono troppi programmi ragionevoli che non è possibile generare se consenti solo il tipo generico della raccolta come tipo di parametro. L'esempio da lui fornito è un'intersezione di List di Number se a Long di <=> s

Altri suggerimenti

remove() (in Map nonché in Collection) non è generico perché dovresti essere in grado di passare qualsiasi tipo di oggetto a remove(o). L'oggetto rimosso non deve essere dello stesso tipo dell'oggetto che si passa a e; richiede solo che siano uguali. Dalla specifica di (o==null ? e==null : o.equals(e)), true rimuove l'oggetto o in modo che equals() sia Object. Si noti che non è necessario che lo stesso tipo sia List.equals() e List. Ciò deriva dal fatto che il metodo Map<ArrayList, Something> accetta un LinkedList come parametro, non solo lo stesso tipo dell'oggetto.

Anche se, può essere comunemente vero che molte classi hanno <=> definito in modo tale che i suoi oggetti possano essere uguali a quelli della propria classe, non è certamente sempre così. Ad esempio, la specifica per <=> afferma che due oggetti Elenco sono uguali se sono entrambi Elenchi e hanno gli stessi contenuti, anche se sono implementazioni diverse di <=>. Quindi, tornando all'esempio in questa domanda, è possibile avere un <=> e per me chiamare <=> con un <=> come argomento, e dovrebbe rimuovere la chiave che è un elenco con lo stesso contenuto. Ciò non sarebbe possibile se <=> fosse generico e ne limitasse il tipo di argomento.

Perché se il parametro type è un carattere jolly, non è possibile utilizzare un metodo di rimozione generico.

Mi sembra di aver incontrato questa domanda con il metodo get (Object) di Map. Il metodo get in questo caso non è generico, anche se dovrebbe ragionevolmente prevedere di passare un oggetto dello stesso tipo del primo parametro di tipo. Mi sono reso conto che se stai passando su Maps con un carattere jolly come primo parametro di tipo, allora non c'è modo di estrarre un elemento dalla Mappa con quel metodo, se quell'argomento fosse generico. Gli argomenti con caratteri jolly non possono davvero essere soddisfatti, poiché il compilatore non può garantire che il tipo sia corretto. Suppongo che il motivo per cui l'aggiunta sia generica sia che ci si aspetta che il tipo sia corretto prima di aggiungerlo alla raccolta. Tuttavia, quando si rimuove un oggetto, se il tipo non è corretto, non corrisponderà comunque a nulla. Se l'argomento fosse un carattere jolly, il metodo sarebbe semplicemente inutilizzabile, anche se potresti avere un oggetto che puoi GARANTIRE appartiene a quella raccolta, perché hai appena ottenuto un riferimento ad esso nella riga precedente ....

Probabilmente non l'ho spiegato molto bene, ma mi sembra abbastanza logico.

Oltre alle altre risposte, c'è un altro motivo per cui il metodo dovrebbe accettare un Object, che è predicati. Considera il seguente esempio:

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);
        }});
    }
}

Il punto è che l'oggetto che viene passato al metodo remove è responsabile della definizione del metodo equals. Costruire predicati diventa molto semplice in questo modo.

Assumere uno ha una collezione di Cat, e alcuni riferimenti a oggetti di tipi Animal, Cat, SiameseCat, e Dog.Chiedendo la raccolta se contiene l'oggetto a cui si riferisce il Cat o SiameseCat riferimento sembra ragionevole.Chiedendo se contiene l'oggetto a cui si riferisce il Animal riferimento può sembrare azzardato, ma è ancora perfettamente ragionevole.L'oggetto in questione potrebbe essere un Cat, e potrebbe essere visualizzato nella raccolta.

Inoltre, anche se l'oggetto sembra essere qualcosa di diverso da un Cat, non c'è nessun problema detto che compare nella raccolta--risposto semplicemente "no, non è così".Una ricerca "in stile" raccolta di qualche tipo deve essere in grado di significativamente accettare alcun riferimento supertype e determinare se l'oggetto è presente all'interno della collezione.Se in passato oggetto di riferimento è di un altro tipo, non c'è nessun modo la collezione potrebbe contenere, in modo che la query è in un certo senso non significativo (sarà sempre rispondere "no").Tuttavia, poiché non vi è alcun modo per limitare i parametri per essere sottotipi o supertipi, è più pratico e semplice accettare qualsiasi tipo e rispondere "no" per tutti gli oggetti il cui tipo non è correlato a quello della raccolta.

Ho sempre pensato che ciò fosse dovuto al fatto che remove () non ha motivo di preoccuparsi del tipo di oggetto che gli dai. È abbastanza facile, a prescindere, controllare se quell'oggetto è uno di quelli che la Collezione contiene, poiché può chiamare equals () su qualsiasi cosa. È necessario controllare il tipo su add () per assicurarsi che contenga solo oggetti di quel tipo.

Rimuovi non è un metodo generico, pertanto il codice esistente che utilizza una raccolta non generica verrà comunque compilato e avrà lo stesso comportamento.

Vedi http://www.ibm.com/developerworks/ java / library / j-jtp01255.html per i dettagli.

Modifica: un commentatore chiede perché il metodo add sia generico. [... rimosso la mia spiegazione ...] Il secondo commentatore ha risposto alla domanda di firebird84 molto meglio di me.

Un altro motivo è dovuto alle interfacce. Ecco un esempio per mostrarlo:

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 */
}

Perché spezzerebbe il codice esistente (pre-Java5). per es.,

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

Ora potresti dire che il codice sopra è sbagliato, ma supponi che o provenga da un insieme eterogeneo di oggetti (cioè conteneva stringhe, numero, oggetti, ecc.). Vuoi rimuovere tutte le corrispondenze, il che era legale perché rimuovere avrebbe semplicemente ignorato le non stringhe perché non uguali. Ma se lo fai rimuovi (String o), non funziona più.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top