2013-07-23 13 views
6

W kontekście statycznych metod ja tutaj wąski jako referencję typu i wywoływać bardziej szczegółowy sposób przedmiotu, jak to:sposób Wywołanie których parametr ten jest ograniczony przez rodzaj przecięcia

public static <T, L extends List<? extends T> & RandomAccess> void do(L list) { 
    // Do some stuff 
} 

public static <T> void do(Iterable<? extends T> iterable) { 
    if(iterable instanceof List && iterable instanceof RandomAccess) 
     // invoke do(List&RandomAccess) method 
    else 
     // do something else 
} 


więc chciałbym wiedzieć, czy istnieje składnia umożliwiając zadzwonić zrobić (iterable) raczej korzystania z niektórych siekać jak ten:

private static <L extends List<? extends T> & Comparable> L cast(Iterable<? extends T> iterable) { 
    return (L) iterable; 
} 

public static <T> void do(Iterable<? extends T> iterable) { 
    if(iterable instanceof List && iterable instanceof RandomAccess) 
     do(cast(iterable)); 
    else 
     // do something else 


UWAGA: wiem, że to nie jest możliwe, aby rzucić mój iterable ten sposób

do((List<? extends T> & RandomAccess) iterable); 

i szwy mi się, że wymazać z L w

L extends List<? extends T> & Comparable 

jest

List<? extends T> 


Więc dlaczego nie mogę wywołać metodę w ten sposób?

do((List<? extends T>) iterable); // Which results in invoking do(Iterable<? extends T) 
+3

kluczowym słowem 'do' jest dla pętli –

+0

Oczywiście ... To był tylko przykład ... –

+0

Wydaje się' & RandomAccess> 'może być uproszczone do' i RandomAccess> 'lub' & RandomAccess> ', przynajmniej dla tych sygnatur. –

Odpowiedz

2

Twoje założenie jest poprawne: wymazywanie skrzyżowaniach jest typem pierwszy - tj List (dla celów refleksji etc).

Możesz otrzymać kod do kompilacji bez „hack” poprzez dostarczanie rodzaj skrzyżowania, do których oddanych jako typ metoda:

public static <T, L extends List<? extends T> & RandomAccess> void method(L list) { 
    // do whatever 
} 

@SuppressWarnings("unchecked") // needed to suppress unsafe cast warning 
public static <T, L extends List<? extends T> & RandomAccess> void method(Iterable<? extends T> iterable) { 
    if(iterable instanceof List && iterable instanceof RandomAccess) 
     method((L)iterable); // calls the other method 
    else 
     return; // do whatever 
} 

Ten kod kompiluje i druga metoda zwraca pierwszą metodę, jak pożądany.

Nie ma sposobu na rzucenie na skrzyżowanie bez tej techniki.


pamiętać, że kod nie kompiluje bo do jest kluczowe java, a zatem nie jest poprawną nazwą metody. Zamiast tego użyłem method(), aby uzyskać przykład kompilacji.

Sądzę, że miałeś na myśli RandomAccess gdzie kodowałeś Comparable.

+0

Uwielbiam proste rozwiązania, jak w tym przypadku, dodając typy ogólne "L" do "metody (Iterable ..)". +1 – Pshemo

+0

Najpierw myślałem o twoim rozwiązaniu i odrzuciłem je, ponieważ nie spodobał mi się ten dodatkowy parametr: brzmiało to jak przerwa w enkapsulacji. Chociaż po przeczytaniu twojego posta, muszę przyznać, że to rozwiązanie jest krótkie i pragmatyczne =) –

+0

To jest zrobione =) Kiedy poprosiłem o _syntax_, myślę, że to odpowiedź najlepiej =) –

1

AFAIK usunięciem L extends List<? extends T> & RandomAccess byłoby List, ale nawet jeśli się mylę, nie można używać (List<? extends T>) iterable ponieważ nie spełnia wymagań (a List niekoniecznie jest RandomAccess). Dlatego jedyną możliwą do zastosowania metodą byłaby wersja Iterable.

Edytuj: szybki wydruk refleksji potwierdza moje przypuszczenie. Nazwałam metody method (oszczędny, prawda?) I pojawia się następujący komunikat:

... method(java.util.List) 
... method(java.lang.Iterable) 

Możecie teraz myślę, że odlewy do List byłaby wystarczająca (i to może być, jeśli chcesz wyłączyć rodzajowych lub wywołaj metodę za pomocą odbicia), ale kompilator nie cierpi z powodu wymazywania tekstu i dlatego wie, że tylko method(java.lang.Iterable) pasuje.

+0

Jak się domyślacie w swojej edycji, chciałbym móc go wywołać tylko za pomocą wymazania. Uważam to wszystko za paradoksalne, pokazując, że brakuje jakiegoś elementu w tym języku. –

+0

@AntoineMarques masz rację. IMHO _erasure_ to zepsuta funkcja, lub inaczej mówiąc, brakuje jej prawdziwych generycznych programów wykonawczych. Zostało to jednak pominięte ze względu na kompatybilność wsteczną. – Thomas

1

Nie sądzę, istnieje sposób, aby wywołać usunięte wersję statycznej metody (w czystym kompilacji)

public static <L extends List<?> & RandomAccess> void do1(L list) {} 

private static <L extends List<?> & RandomAccess> L cast(Iterable<?> iterable) 
{ 
    return (L) iterable; 
} 

public static void do2(Iterable<?> iterable) 
{ 
    do1(cast(iterable)); 

    // try to invoke the erased version 
    //do1((List)iterable); // does not compile 
} 

Możemy powołać wersję usunięte z przykład metod przez surowego typu

static class Helper<T> 
{ 
    <T, L extends List<? extends T> & RandomAccess> void do3(L list) 
    { 
     do1(list); 
    } 
} 

public static void do2(Iterable<?> iterable) 
{ 
    Helper helper = new Helper(); // raw type 
    helper.do3((List) iterable); // erased method 
} 

Powrót do metod statycznych. Załóżmy, że mamy ten kod pre-java5

#1 static void foo(List list){} 

#2 foo(new ArrayList()); 

teraz po java5, # 1 jest generified jak

#1 static void foo(List<?> list){} 

ale # 2 jest przechowywany jak jest z surowych typu. Jak to możliwe, że # 2 wciąż się kompiluje? Specyfikacja (15.12.2.2) umożliwia tę operację, umożliwiając niezaznaczoną konwersję. Niezaznaczona konwersja może przekonwertować surowe Foo na Foo<..> (ale nie bardziej wyrafinowane jak Foo -> Foo<..>&Bar).

Specyfikacja zawiera tylko tyle lew, aby upewnić się, że starszy kod może przetrwać typowe generowanie. Twoja sprawa z pewnością nie jest typowa, a sztuczki nie mają zastosowania.

+0

+1 Bardzo interesujące. –

+0

Nie wiedziałem o tej funkcji. Dziękujemy za udostępnienie =) –

Powiązane problemy