Deklaration der Vergleichsschnittstelle

9

Wenn die Schnittstelle Comparable generisch gemacht wurde, wurde die Deklaration

%Vor%

Eigentlich sollte es so etwas wie

sein %Vor%

Es macht keinen Sinn, dass T Comparable nicht erweitert, weil die Implementierer das sicherstellen müssen

%Vor%

und

%Vor%

haben immer umgekehrte Vorzeichen.

Ich habe immer angenommen, dass der Grund dafür, die Erklärung "falsch" zu bekommen, etwas mit den Problemen der Vereinheitlichung einer bestehenden Schnittstelle zu tun hat, aber ich kann es nicht wirklich erklären.

Hat jemand irgendwelche Einsichten?

    
Paul Boddington 22.10.2017, 19:33
quelle

1 Antwort

3
  

Eigentlich sollte es so etwas wie

sein      

interface Comparable<T extends Comparable<T>>

Aber das gibt dir wirklich nichts, was du nicht mit interface Comparable<T> bekommst.

Sie erwähnen die Sache mit a.compareTo(b) und b.compareTo(a) , aber beachten Sie, dass Ihre Deklaration ( interface Comparable<T extends Comparable<T>> ) nicht wirklich sicherstellt, dass, wenn a.compareTo(b) gültig ist, diese b.compareTo(a) kompiliert. Wenn a ist Comparable<T> , erfordert a.compareTo(b) , dass b be T ist, was auch Comparable<T> ist, also nimmt b.compareTo() eine T . Das beweist nicht, dass b.compareTo(a) funktioniert, da a ein Comparable<T> ist. Betrachten wir zum Beispiel class Foo implements Comparable<Foo> und class Bar implements Comparable<Foo> . Wenn a vom Typ Bar und b vom Typ Foo ist, dann wird mit Ihrer Deklaration von Comparable , a.compareTo(b) kompiliert, aber b.compareTo(a) kompiliert nicht (was auch dasselbe ist passiert mit der ursprünglichen Deklaration von Comparable ). Um eine Grenze zu haben, die garantiert, dass, wenn a.compareTo(b) funktioniert, auch b.compareTo(a) funktioniert, müssten Sie etwas wie interface Comparable<T extends Comparable<Comparable<T>>> haben, aber das wäre eine nutzlose Schnittstelle, denn niemand hat T , das ist vergleichbar mit Comparable<T> .

Grundlegend ist der Zweck des Hinzufügens einer Grenze in Generics, Ihnen zu ermöglichen, etwas zu kompilieren, das nicht kompilieren würde oder eine Umwandlung ohne die Grenze erfordern würde. Denken Sie daran, Java überprüft, dass Code zur Kompilierzeit typsicher ist - es ermöglicht Ihnen nur das Kompilieren von etwas, von dem es weiß, dass es mit den zur Kompilierungszeit deklarierten Typen möglich ist (es sei denn, Sie fügen explizite Umwandlungen hinzu) die Richtigkeit der Konvertierung). Das Hinzufügen einer Grenze erhöht also nicht die Typensicherheit. Mit oder ohne die Grenze kompiliert der Java-Compiler nur Code, der nachweislich typsicher ist (außer bei expliziten Umwandlungen oder unformatierten Konvertierungen). Der Unterschied, den eine Bindung hat, ist, dass Java durch die Hinzufügung von Constraints more -Code als typsicher akzeptiert, da die Constraints Java erlauben, die Korrektheit abzuleiten, wo es vorher nicht möglich war. Daher sollte eine Grenze nur dann hinzugefügt werden, wenn Sie damit etwas kompilieren können, das Sie nicht ohne die Grenze verwenden könnten, oder Sie müssten explizite Umwandlungen ohne die Grenze hinzufügen. Was bringt es sonst, Komplexität ohne Nutzen hinzuzufügen?

Hier werden Sie keine realistischen Anwendungsfälle von Code finden, die mit Ihrer Deklaration von Comparable kompiliert würden, aber nicht mit der ursprünglichen Deklaration kompilieren würden. Die einzigen realistischen Anwendungsfälle, die ich gesehen habe, wo eine Deklaration interface Foo<T extends Foo<T>> erlauben würde etwas zu kompilieren, was nicht mit interface Foo<T> in etwas wie dem Builder Muster wäre, wobei Foo<T> eine Methode hat, die T zurückgibt, und da wir wissen, dass T ein Untertyp von Foo<T> ist, können wir von einem Foo<T> zu einem Foo<T> gehen und diese Operationen verketten, ohne den spezifischen Typ von T zu kennen. Aber das ist nicht der Fall mit Comparable - Comparable hat keine Methode, die T zurückgibt.

Wenn Sie eine Klasse oder Methode haben, die einen vergleichbaren Typ annimmt, den sie sortieren oder ordnen müssen, muss es eine generische Methode oder Klasse sein, die erfordert, dass der Typ mit sich selbst vergleichbar ist (zB class SortedList<T extends Comparable<? super T>> oder %Code%). Keine Beschränkung in der Deklaration von <T extends Comparable<? super T>> void sort(List<T>) kann sicherstellen, dass der Typ mit sich selbst vergleichbar ist.

Wenn jemand beschließt, eine Klasse zu schreiben, in der ein Typ mit etwas völlig anderem verwandt ist (sogar etwas, das nicht Comparable ist), dann ist das aus Sicht der Typsicherheit in Ordnung. Ja, Sie haben darauf hingewiesen, dass es wahrscheinlich gegen die Dokumentation von Comparable verstößt, aber das ist ein Code-Verhalten, kein Typ-Sicherheitsproblem. Ihre Klasse wird wahrscheinlich nicht sehr nützlich sein, da sie die Grenzen der meisten Orte, die .compareTo() verwenden, nicht erfüllen wird, was wahrscheinlich Grenzen haben wird, die erfordern, dass der Typ mit sich selbst vergleichbar ist. Wenn es einen Ort gibt, der Comparable braucht, der keine Grenze hat, die den Typ mit sich selbst vergleichbar macht, dann können sie ihre Klasse dort verwenden, aber das ist immer noch typsicher, weil die Tatsache, dass der Ort Wenn der Typ mit sich selbst vergleichbar sein soll, bedeutet dies, dass sich sein eigener Code nicht auf diese Tatsache stützt, um sicher zu kompilieren (oder sie verwendeten explizite Umwandlungen, in welchem ​​Fall sie die Verantwortung für die Korrektheit übernehmen). In jedem Fall hat jemand, der potenziell eine (meist nutzlose und vielleicht gegen die Dokumentation verstoßende) Klasse schreibt, die vergleichbar ist mit etwas, das nicht vergleichbar ist, keinen Einfluss auf Ihre Fähigkeit, eine eigene Klasse zu schreiben, die mit sich selbst vergleichbar ist, und es gibt keine Typsicherheit Grund, irgendwelche Grenzen hinzuzufügen.

    
newacct 02.11.2017 04:38
quelle

Tags und Links