2011-06-23 11 views
14

Próbuję napisać ogólną funkcję maksimum, która zajmuje dwa Comparable s.Jak zaimplementować ogólną funkcję `max (Comparable a, Comparable b)` w Javie?

tej pory mam

public static <T extends Comparable<?>> T max(T a, T b) { 
    if (a == null) { 
     if (b == null) return a; 
     else return b; 
    } 
    if (b == null) 
     return a; 
    return a.compareTo(b) > 0 ? a : b; 
} 

To nie kompiluje z

The method compareTo(capture#5-of ?) in the type Comparable<capture#5-of ?> is not applicable for the arguments (T) 

Co myślę, że to jest o to, że, że ? w Comparable<?> może być interpretowane jako jeden typ parametru a, i drugi dla parametru b, aby nie można było ich porównywać.

Jak wydobyć się z tej dziury?

+0

W bloku 'if (a == null)' nie ma potrzeby stosowania zagnieżdżonej klauzuli if. Zmniejszenie bloku do 'return b;' daje taki sam wynik ('null', gdy' b' ma wartość 'null',' b' w innym przypadku). – benjamin

+0

Zobacz [org.apache.commons.lang3.ObjectUtils.compare] (https://commons.apache.org/proper/commons-lang/javadocs/api-3.6/org/apache/commons/lang3/ObjectUtils.html# compare-TT-) –

Odpowiedz

24

Aby uzyskać najlepsze wyniki, należy użyć public static <T extends Comparable<? super T>> T max(T a, T b).

Problem z <T extends Comparable<?>> polega na tym, że mówi się, że typ T jest porównywalny z pewnym typem, ale nie wiesz, jaki to typ. Oczywiście, zdrowy rozsądek dyktowałby, że klasa, która implementuje porównywalne, powinna być w stanie być porównywalna z samą sobą (tj. Być zdolna do porównania z obiektami własnego typu), ale nie ma żadnych przeszkód, aby klasa A mogła wdrożyć Comparable<B>, gdzie A i B nie mają ze sobą nic wspólnego. <T extends Comparable<T>> rozwiązuje ten problem.

Ale z tym jest subtelny problem. Załóżmy, że klasa X implementuje Comparable<X>, a ja mam klasę Y, która rozszerza X. Więc klasa Y automatycznie implementuje Comparable<X> przez dziedziczenie. Klasa Y nie może również implementować Comparable<Y>, ponieważ klasa nie może zaimplementować interfejsu dwukrotnie z różnymi parametrami typu. Nie stanowi to problemu, ponieważ instancje Y są instancjami X, więc Y jest porównywalne ze wszystkimi instancjami Y. Problem polega jednak na tym, że nie można użyć typu Y z funkcją <T extends Comparable<T>> T max(T a, T b), ponieważ Y nie implementuje Comparable<Y>. Granice są zbyt surowe.<T extends Comparable<? super T>> rozwiązuje problem, ponieważ wystarczy, aby T było porównywalne z jakimś nadtypem T (który zawierałby wszystkie wystąpienia T). Przypomnijmy regułę PECS - producent extends, konsument super - w tym przypadku, Comparable jest konsumentem (przyjmuje obiekt do porównania), więc super ma sens.

Jest to granica typu używana przez wszystkie funkcje sortowania i zamawiania w bibliotece Java.

4

Otrzymujesz ten błąd, ponieważ Comparable<?> zasadniczo mówi, że jest porównywalny z czymś, co nie ma żadnych szczegółów. Zamiast tego powinieneś napisać Comparable<T>, aby kompilator wiedział, że typ T jest porównywalny z samym sobą.

+0

Dzięki - teraz, aby rozwiązać prawdziwy problem, który miał przenieść go do Scala! –

1

Odpowiadając na moje własne pytanie z linków generowanych przez SO, wydaje mi się, że jest to subtelny duplikat Fun with Java generics, chociaż wydaje mi się, że nie można mnie winić za to, że nie znalazłem tego tytułu!

Najprostszym rozwiązaniem wydaje się być

public static <T extends Comparable<T>> T max(T a, T b) { 
    if (a == null) { 
     if (b == null) return a; 
     else return b; 
    } 
    if (b == null) 
     return a; 
    return a.compareTo(b) > 0 ? a : b; 
} 
1

Pisałem klasę użytkową do tego. Może okazać się przydatna (biblioteka jest Open Source):

http://softsmithy.sourceforge.net/lib/docs/api/org/softsmithy/lib/util/Comparables.html

Strona domowa:

http://www.softsmithy.org

Download:

http://sourceforge.net/projects/softsmithy/files/softsmithy/

Maven:

<dependency> 
    <groupid>org.softsmithy.lib</groupid> 
    <artifactid>lib-core</artifactid> 
    <version>0.1</version> 
</dependency> 
+0

Fajnie, dzięki. To jest rzecz, która mnie gobsmacked nie jest częścią standardowej biblioteki, biorąc pod uwagę ile kodu potrzeba i jak łatwo jest się pomylić. –

+0

Uwaga: Właśnie sprawdziłem kod źródłowy: obecnie nie obsługuje on wartości pustych: http://softsmithy.hg.sourceforge.net/hgweb/softsmithy/lib/main-golden/file/5c4db802573b/lib-core/src /main/java/org/softsmithy/lib/util/Comparables.java – Puce

+0

:-) Dzięki za czystość - jestem pewien, że nie miałbym, gdybym nie miał do czynienia z domeną, w której wiele dat mogło nie być dostarczone. –

Powiązane problemy