2010-06-08 5 views
14

Ist die Rückwärtskompatibilität mit älteren (nicht generischen) Versionen von Collection aufrechtzuerhalten? Oder gibt es ein subtileres Detail, das mir fehlt? Ich sehe dieses Muster wiederholt in remove auch (remove(Object o)), aber add wird als add(E e) generalisiert.Warum haben wir enthält (Objekt o) statt enthält (e e)?

+2

möglich Duplikat von [Warum sind Java-Sammlungen nicht Methoden generisch entfernen?] (Http://StackOverflow.com/questions/104799/why-arent-java-collections-remove-methods-generic) – newacct

Antwort

10

benötigt eine Object, weil das Objekt, mit dem es übereinstimmt, nicht vom selben Typ sein muss wie das Objekt, das Sie an übergeben; es erfordert nur, dass sie gleich sind. Von der Spezifikation gibt contains(o) den Wert true zurück, wenn ein Objekt e vorhanden ist, so dass (o==null ? e==null : o.equals(e)) wahr ist.Beachten Sie, dass nichts o und e vom gleichen Typ sein muss. Dies ergibt sich aus der Tatsache, dass die equals()-Methode einen Object als Parameter verwendet, nicht nur den gleichen Typ wie das Objekt.

Obwohl es allgemein zutreffend sein kann, dass viele Klassen equals() definiert haben, so dass ihre Objekte nur Objekten ihrer eigenen Klasse entsprechen können, ist das sicherlich nicht immer der Fall. Zum Beispiel sagt die Spezifikation für List.equals(), dass zwei List Objekte gleich sind, wenn sie beide List s sind und den gleichen Inhalt haben, auch wenn sie verschiedene Implementierungen von List sind. Also zurück zu dem Beispiel in dieser Frage, ist es möglich, eine Collection<ArrayList> und für mich mit einem LinkedList als Argument zu haben, und es möglicherweise True zurückgegeben, wenn es eine Liste mit dem gleichen Inhalt ist. Dies wäre nicht möglich, wenn generisch wäre und sein Argumenttyp auf E beschränkt wäre.

In der Tat, die Tatsache, dass jedes Objekt als Argument erlaubt eine interessante Anwendung, wo Sie können, um es für die Existenz eines Objekts in der Sammlung zu testen, die eine bestimmte Eigenschaft erfüllt:

Collection<Integer> integers; 
boolean oddNumberExists = integers.contains(new Object() { 
    public boolean equals(Object e) { 
     Integer i = (Integer)e; 
     if (i % 2 != 0) return true; 
     else return false; 
    } 
}); 
+3

Auch diese Methodensignatur 'enthält (Objekt o)' bietet eine wirklich schöne Waffe um sich bequem in einen Fuß zu schießen. Diese Methode ist wirklich ein Paradebeispiel für die Einschränkung von Java Generics. –

4

Es ist, weil die contains Funktion die equals Funktion nutzt, und die equals Funktion in der Basisobjektklasse mit einer Signatur von equals(Object o) anstatt equals(E e) definiert (da nicht alle Klassen sind generisch). Gleicher Fall mit der Funktion remove - es durchläuft die Sammlung mit der equals-Funktion, die ein Object-Argument übernimmt.

Dies erklärt die Entscheidung jedoch nicht direkt, da sie möglicherweise noch den Typ E verwendet hätten und es automatisch für die Eingabe von Object beim Aufruf von equals umgewandelt hätte; aber ich stelle mir vor, sie wollten die Funktion für andere Objekttypen aufrufen lassen. Es ist nichts falsch daran, eine Collection<Foo> c; und dann rufen c.contains(somethingOfTypeBar) - es wird immer falsch, und so beseitigt es die Notwendigkeit für eine Besetzung zu Foo (die eine Ausnahme auslösen kann) oder, um vor der Ausnahme zu schützen, eine typeof Anruf. Sie können sich also vorstellen, wenn Sie über etwas mit gemischten Typen iterieren und contains für jedes der Elemente aufrufen, können Sie einfach die contains-Funktion für alle verwenden, anstatt Wächter zu benötigen.

Es ist eigentlich erinnert an den „neueren“ lose typisierte Sprachen, wenn Sie es sehen auf diese Weise ...

+0

Dies hat nichts zu tun mach mit dem wahren Grund. Es wäre viel schöner, eine Art von Typprüfung auf 'contains' und vielleicht' remove' zu ​​haben. Es wäre viel weniger fehleranfällig gewesen. –

+3

"es wird immer falsch zurückgegeben" nein wird es nicht. es ist durchaus möglich, dass eine Klasse ein Objekt einer anderen Klasse egalisiert – newacct

5

hier beantwortet.
Why aren't Java Collections remove methods generic?
Kurz gesagt, sie wollten die Rückwärtskompatibilität maximieren, weil Sammlungen lange vor Generika eingeführt wurden.

Und von mir hinzufügen: das Video, das er bezieht, ist es wert, zu sehen.
http://www.youtube.com/watch?v=wDN_EYUvUq0

Update
Um zu klären, der Mann, der sagte, dass (im Video) war einer der Menschen, die Java-Karten und Sammlungen aktualisiert Generika zu verwenden. Wenn er es nicht weiß, wer dann?

0

Da es sonst nur mit der genauen Übereinstimmung des Parametertyps verglichen werden könnte, hätten speziell wildbehinderte Sammlungen nicht mehr funktioniert, z.

class Base 
{ 
} 

class Derived 
    extends Base 
{ 
} 

Collection< ? extends Base > c = ...; 

Derived d = ...; 

Base base_ref = d; 

c.contains(d); // Would have produced compile error 

c.contains(base_ref); // Would have produced compile error 

EDIT
Für Zweifler, die denken, das ist nicht einer der Gründe dafür, hier wird eine modifizierte Array-Liste mit einem generified würde enthält Methode

class MyCollection<E> extends ArrayList<E> 
{ 
    public boolean myContains(E e) 
    { 
     return false; 
    } 
} 

MyCollecttion< ? extends Base > c2 = ...; 

c2.myContains(d); // does not compile 
c2.myContains(base_ref); // does not compile 

Grundsätzlich contains(Object o) ist ein Hack zu machen dieser sehr häufige Anwendungsfall, um mit Java Generics zu arbeiten.

+0

Pflege, um downvote zu erklären? –

+0

Im normalen Szenario würde niemand eine Sammlung in 'Collection 'während der Elementmanipulation. Wenn dies der Fall ist, wäre die 'add (E e)' Methode ebenfalls ein Fehler gewesen. – Jai

+0

@Jai. Hier ist ein Szenario 'void myMethod (Sammlung coll)'. Wenn Sie innerhalb dieser Sammlung sehen müssen, ob eine Verison von 'Foo' in dieser Sammlung enthalten ist, würden Sie die in dieser Antwort beschriebene Einschränkung erreichen. –

0

"enthält dieser Korb Äpfel diese Orange?"

klar eine echte Antwort kann nicht gegeben werden. aber das lässt immer noch zu möglichkeiten:

  1. die antwort ist FALSCH.
  2. die Frage ist nicht gut gebildet, sollte es nicht kompilieren übergeben.

die Sammlung api wählte die erste. aber die 2. Wahl wäre auch vollkommen sinnvoll. Eine Frage wie diese ist 99,99% der Fälle eine Schwachsinnfrage, also fragt nicht einmal!

+0

Die Frage sollte nicht als schlecht geformt angesehen werden, wenn z.B. hat zwei Körbe, die Früchte enthalten und will wissen, ob Früchte in einem Korb mit Früchten in dem anderen übereinstimmen. Wenn man wüsste, dass ein Korb nur Äpfel und nur Orangen enthält, kann es sinnvoll sein, die Suche nach einem Streichholz kurzzuschließen, aber man nimmt an, dass ein Korb nur Äpfel und der andere nur gemischte Früchte enthielt. Den Apfelkorb nach jeder Frucht im anderen Korb zu fragen, ohne zuerst seinen Typ zu überprüfen, wäre einfacher, als jede Frucht zu prüfen, bevor man fragt, ob es ein Apfel ist. – supercat

+0

In meinem Beispiel ist der Parameter bekanntlich eine Orange, daher klingt die Frage lächerlich. In Ihrem Beispiel wäre die Frage: "Hat der Korb mit Äpfeln diese Frucht?", das ist legitim.der Anwendungsfall ist jedoch seltener. Wir können die API so umgestalten, dass Sie Ihre Frage zulassen und gleichzeitig meine Frage verbieten können, indem Sie den Parametertyp auf Super-Apple-Typen beschränken. – irreputable

+0

Es gibt keinen Mechanismus in .Net, über den eine Methode einen generischen oder anderen Parameter als Supertyp eines anderen Typs einschränken kann, und ich denke nicht, dass Java auch einen solchen Mechanismus besitzt. Solch eine Sache würde dazu neigen, das Liskow-Substitutionsprinzip zu verletzen, da eine Methode, die mit 'Fruit' verwendet werden kann, mit' Orange' verwendet werden kann. Es gibt Möglichkeiten in .Net, über die man "Veraltete" Tags verwenden könnte, um Compiler-Squawks in bestimmten statisch identifizierbaren, dummen Fällen auszulösen, aber ich würde denken, dass das verwirrender als hilfreich wäre. – supercat

Verwandte Themen