2017-12-09 3 views
1

Betrachten Sie das folgende Snippet:In Java, leere Sammlungen des gleichen Typs, aber unterschiedliche Typ Parameter immer gleich?

List<String> list1 = new ArrayList<>(); 
    List<Integer> list2 = new ArrayList<>(); 
    assertThat(list1.equals(list2), is(true)); 

Die Behauptung folgt.

der Tat, soweit ich es verstehe, gibt es keine Möglichkeit in Java, für die equals Verfahren zwischen den beiden, aufgrund zu unterscheiden Typ-Löschung zu Laufzeit (dh die Tatsache, dass die Typargumente String und Integer sind nur zugänglich zur Kompilierzeit, aber nicht zur Laufzeit). Und da es keine Elemente gibt, die verglichen werden könnten, muss equals wahr zurückgeben. Und bei dieser Denkrichtung muss das für alle Sammlungen stimmen.

Also die Frage ist die folgende: Ist mein Denkprozess korrekt, oder fehlt mir etwas?

EDIT

@qqilihq ein ausgezeichnetes answer gab, die die nächste Frage aufgefordert: Ist dies möglich, ohne vorbei den Typen explizit zu erreichen (ich bin in Ordnung, wenn der Typ gespeichert ist ausdrücklich . irgendwie, nur der Benutzer hat sollte es nicht passieren müssen)

ich versucht, die folgenden, aber es hat nicht funktioniert (ich denke, durch Löschen geben):

public TypedList(List<T> delegate) { 
     this.delegate = Objects.requireNonNull(delegate); 
     this.type = (Class<T>) delegate.getClass(); 
    } 

Aber vielleicht können Sie etwas Ähnliches tun? this.type = T. (Dies wird nicht kompiliert, aber möglicherweise ist etwas Ähnliches möglich.)

Antwort

3

Wie Sie feststellen, gehen die Typinformationen zur Laufzeit in Ihrem Beispiel verloren. Aber in Bezug auf Ihre Frage in den Kommentaren:

können Sie auch ein Gegenbeispiel, d. H. Eine Sammlung, wo zwei leere Instanzen von verschiedenen generischen Typen nicht als gleich behandelt werden?

Sie können natürlich implementieren Ihre eigenen Collection die tut berücksichtigen die Typinformationen in der equals Methode. Hier ist ein sehr einfaches Beispiel ein TypedList Wrapper um eine beliebige List erstellen:

static final class TypedList<T> extends AbstractList<T> { 
    private final List<T> delegate; 
    private final Class<T> type; 

    public TypedList(List<T> delegate, Class<T> type) { 
     this.delegate = Objects.requireNonNull(delegate); 
     this.type = Objects.requireNonNull(type); 
    } 

    @Override 
    public T get(int index) { 
     return delegate.get(index); 
    } 

    @Override 
    public int size() { 
     return delegate.size(); 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (!super.equals(obj)) { 
      return false; 
     } 
     // Lists are equal, now additionally, check the type 
     TypedList<?> other = (TypedList<?>) obj; 
     return this.type.equals(other.type); 
    } 

    // hashCode omitted for brevity 
} 

Beachten Sie, dass Sie benötigen den Typ explizit zu speichern, aufgrund der Art Löschung erwähnt. Verbrauch:

List<String> list1 = new ArrayList<>(); 
List<Integer> list2 = new ArrayList<>(); 
System.out.println(list1.equals(list2)); // true 

list1 = new TypedList<>(list1, String.class); 
list2 = new TypedList<>(list2, Integer.class); 
System.out.println(list1.equals(list2)); // false 
+0

+1, das ist eine Art, an die ich nicht gedacht habe. Nun, die nächste Frage wäre, dass das möglich ist, um dies zu erreichen, ohne den Typ explizit als Parameter zu übergeben (mir geht es gut, wenn Sie es irgendwie aus 'delegate' extrahieren. Aber ich habe es versucht und gescheitert.) Ich werde die Frage mit diesem neuen Detail bearbeiten. – Attilio

+2

Kurze und negative Antwort: Es ist nicht möglich ** ohne ** den expliziten Typ Token. Die 'Liste ' wird zur Laufzeit praktisch nur eine 'List' sein, so dass Informationen einfach nicht da sind. Für den Typ in 'Class ' ist es anders, da es sich um einen" First-Class-Citizen "handelt - auch wenn es zur Laufzeit nur' Class' ist, es enthält immer noch die angegebene Type-Info (zB 'String.class', ...) – qqilihq

+0

Randnotiz: 'java.util.Collections' folgt genau dem gleichen Prinzip, z In dieser Methode: 'java.util.Collections.checkedList (Liste , Klasse )' - Der Typparameter muss explizit als Argument übergeben werden. – qqilihq

0

Es hängt wirklich davon ab, wie einzelne Untergruppe, die die Auflistungsschnittstelle implementieren, die equals() -Methode behandelt. Für Arraylist ist die Umsetzung in AbstractList welche wie folgt:

public boolean equals(Object o) { 
    if (o == this) 
     return true; 
    if (!(o instanceof List)) 
     return false; 

    ListIterator<E> e1 = listIterator(); 
    ListIterator<?> e2 = ((List<?>) o).listIterator(); 
    while (e1.hasNext() && e2.hasNext()) { 
     E o1 = e1.next(); 
     Object o2 = e2.next(); 
     if (!(o1==null ? o2==null : o1.equals(o2))) 
      return false; 
    } 
    return !(e1.hasNext() || e2.hasNext()); 
} 

So einfach es wahr zurück, wenn das entgegengesetzte Objekt ist auch eine Liste und die Größe ist gleich und jedes Element ist gleich. Wenn also beide Listen leer sind, muss das Ergebnis wahr sein.

+0

„Es hängt wirklich davon ab, wie die einzelnen sublcass zurückkehren wird, dass die Sammlung Schnittstelle übernimmt die Gleichen implementieren () Methode." Nun, was ich frage ist, ob es wirklich überhaupt möglich ist, 'equals' anders zu behandeln, als' ArrayList'. Mit anderen Worten: Können Sie auch ein Gegenbeispiel zeigen, d. H. Eine Sammlung, in der zwei leere Instanzen unterschiedlicher generischer Typen nicht als gleich behandelt werden? – Attilio

0

Generics für Typ verwendet bei der Kompilierung überprüfen.

Wenn Sie ArrayList wie folgt zu erstellen:

List<String> list1 = new ArrayList<>(); 
List<Integer> list2 = new ArrayList<>(); 
System.out.println(list1.equals(list2)); // result:true 

list1 und list2 wird gleich sein, weil sie leer sind. ArrayList verwendet intern Object[], um ArrayList Eintrag zu speichern.

Wenn Sie einige Eintrag in die Array-Liste hinzufügen:

List<String> list1 = new ArrayList<>(); 
list1.add("A"); 

List<Integer> list2 = new ArrayList<>(); 
list2.add(1); 

System.out.println(list1.equals(list2)); // result: false 

Diese false weil ArrayList Eintrag unterschiedlich sind

+0

Ajit: Ich verstehe im Grunde, wie es funktioniert, und warum, in meinem Fall, kehrt es wahr zurück. Aber ich stimme dieser Aussage nicht zu "list1 und list2 werden gleich sein, weil sie leer sind". Für mich können die beiden Listen nicht ** konzeptionell ** gleich sein, weil sie von verschiedenen Typen sind. Ich würde eher die Tatsache sehen, dass dies nicht als ein Kurzschluß (oder zumindest eine Besonderheit) von Java betrachtet werden kann, und war neugierig, ob es einen Weg dafür gab. – Attilio

Verwandte Themen