2009-08-18 16 views
16

Java często może wnioskować generics na podstawie argumentów (a nawet typu zwracanego, w przeciwieństwie do np. C#).Wnioskowane generatory wieloznaczne w typie zwrotnym

Sprawa w punkcie: Mam rodzajowe klasy Pair<T1, T2> który przechowuje tylko parę wartości i mogą być używane w następujący sposób:

Pair<String, String> pair = Pair.of("Hello", "World"); 

Sposób of wygląda tak:

public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) { 
    return new Pair<T1, T2>(first, second); 
} 

Bardzo ładne. Jednak to nie działa już w następnym użytkowej sprawy, która wymaga symboli wieloznacznych: (. Zwróć uwagę na wyraźny obsady aby List.class poprawny typ)

Pair<Class<?>, String> pair = Pair.of((Class<?>) List.class, "hello"); 

kod nie powiedzie się z powodu następującego błędu (pod warunkiem przez Eclipse):

niezgodność typów: nie można przekonwertować z TestClass.Pair<Class<capture#1-of ?>,String> do TestClass.Pair<Class<?>,String>

Jednak jawne wywołanie konstruktora nadal działa zgodnie z oczekiwaniami:

Pair<Class<?>, String> pair = 
    new Pair<Class<?>, String>((Class<?>) List.class, "hello"); 

Może ktoś wyjaśnić to zachowanie? Czy to projekt? Czy jest to wymagane? Czy robię coś złego, czy natknąłem się na wadę projektu/błędu w kompilatorze?

Dzikie przypuszczenie: the „capture # 1-of?” Jakoś wydaje się sugerować, że wieloznaczny jest wypełniana przez kompilator w locie, dzięki czemu Wpisz Class<List>, a tym samym braku konwersji (od Pair<Class<?>, String> do Pair<Class<List>, String>) . Czy to jest poprawne? Czy istnieje sposób obejścia tego?


pod względem kompletności, albowiem o to uproszczona wersja klasy Pair:

public final class Pair<T1, T2> { 
    public final T1 first; 
    public final T2 second; 

    public Pair(T1 first, T2 second) { 
     this.first = first; 
     this.second = second; 
    } 

    public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) { 
     return new Pair<T1, T2>(first, second); 
    } 
} 
+0

Wygląda na to, że konwerter widzi podpis "of", ponieważ zwraca parę ,? rozszerza klasę > typ. Dla klas końcowych wydaje się dość sprytny, aby zredukować część wydłużoną, dlatego nie narzeka na Ciąg. – Zed

+0

Hmmm, interesujące. Dzięki za linkowanie mnie tutaj. – jjnguy

+0

Teraz działa w java8. Docelowy typ jest również konsultowany w celu wnioskowania. – ZhongYu

Odpowiedz

13

Powodem konstruktor działa to, że jesteś jawnie określając parametry typu. Statyczna metoda też będzie działać, jeśli to zrobisz:

Pair<Class<?>, String> pair = Pair.<Class<?>, String>of(List.class, "hello"); 

Oczywiście, cały powód masz statyczną metodą na pierwszym miejscu jest chyba tylko po to żeby wnioskowanie typu (które nie działa z konstruktorów w wszystko).

Problem tutaj (zgodnie z sugestią) jest to, że kompilator wykonuje capture conversion. Uważam, że to w wyniku [§15.12.2.6 of the JLS]:

  • Typ wynikiem wybranej metody określa się następująco:
    • Jeśli metoda jest wywoływana jest zadeklarowana z typem zwracanej nieważne, wtedy wynik jest nieważny.
    • W przeciwnym razie, jeśli zaznaczona konwersja była konieczna metoda być stosowane wówczas typ wynik jest usunięcie (§4.6) od tejże metody zadeklarowane typ zwracany.
    • W przeciwnym razie, jeśli metoda jest wywoływana jest nazwą rodzajową, to dla 1W, niech Fi być formalne parametry Rodzaj metodę, niech Ai być rzeczywisty typ argumenty wnioskować o sposobie inwokacji i niech R będzie zadeklarował zwracający typ metody będący wołaniem . Typ wyniku uzyskuje się przez zastosowanie konwersji przechwytywania (§5.1.10) na R [F1: = A1, ..., Fn: = An].
    • W przeciwnym razie typ wyniku uzyskuje się przez zastosowanie konwersji przechwytującej (§5.1.10) do typu podanego w deklaracji metody .

Jeśli naprawdę chcesz się wnioskowanie, jedną z możliwych rozwiązaniem jest zrobić coś takiego:

Pair<? extends Class<?>, String> pair = Pair.of(List.class, "hello"); 

Zmienna pair będzie miał szerszy rodzaj, a oznacza to trochę więcej wpisywania w nazwie typu zmiennej, ale przynajmniej nie musisz już przesyłać wywołania metody.

+0

Wielkie dzięki. Nadal zastanawiam się jednak, czy to obejście nie czyni jeszcze bardziej mylącym. Na razie pozostawiam go takim, jaki jest. –