2013-10-02 11 views
9

Próbuję utworzyć ImmutableMap, który mapuje klasy na ciągi znaków (uwaga: to oczywiście przykład!). Jednak cośProblem z tworzeniem ImmutableMap z klasą <?> jako kluczem

ImmutableMap<Class<?>, String> map = ImmutableMap.of( 
    Integer.class, "Integer", 
    Date.class, "Date" 
); 

daje mi następujący błąd

Type mismatch: cannot convert from ImmutableMap<Class<? extends Object&Comparable<?>&Serializable>,String> to ImmutableMap<Class<?>,String> 

dziwo to działa, jeśli dodam oddanych do Class<?> jednego (!) Z kluczy, tzn

ImmutableMap<Class<?>, String> map = ImmutableMap.of(
    Integer.class, "Integer", 
    Date.class, "Date", 
    (Class<?>) String.class, "String", 
    long.class, "Long" 
); 

będzie działać dobrze. Jestem zdziwiony tym zachowaniem: po pierwsze, dlaczego nie działa bez rzutów? Wszystkie te są klasami i naprawdę nie są bardziej ogólne niż Class<?>, więc dlaczego to nie działa? Po drugie, dlaczego obsada jednego z kluczy sprawia, że ​​działa?

(dygresja: jeśli zastanawiacie się, dlaczego nawet chce zrobić coś takiego - tak, to ze względu na odbicie ...)

Edit: I właściwie tylko zorientowali się, że to będzie działać, ale stil bym chciał zrozumieć powyżej zachowanie ruchowe

ImmutableMap<Class<?>, String> map = ImmutableMap.<Class<?>, String>builder() 
    .put(Integer.class, "Integer") 
    .put(Date.class, "Date") 
    .build(); 

Odpowiedz

15

ten sposób kompilator wnioskuje parametrów typu kiedy przechodzą niespójnych argumentów metod. Jeśli zauważysz, metoda ImmutableMap.of(K, V, K, V) używa tego samego parametru typu K zarówno dla Date jak i Integer. Można by pomyśleć, że to powinno zakończyć się niepowodzeniem, ponieważ przekazujemy niespójne argumenty metody, co oznacza, że ​​przekazujemy różne typy dla tego samego typu parametru: K. Ale zaskakująco tak nie jest.

Class<Date> i Class<Integer> są przechwytywania zamienny wszystkim następujące elementy:

  • Class<? extends Object>
  • Class<? extends Serializable>
  • Class<? extends Comparable<?>>

Więc, typ K jest wywnioskować jako mieszanina wszystkich:

K := Class<? extends Object&Serializable&Comparable<?>> 

To jest zwracana wartość metody rzeczywiście byłoby:

ImmutableMap<Class<? extends Object&Serializable&Comparable<?>>, String> 

Oczywiście, nie można przypisać bezpośrednio do ImmutableMap<Class<?>, String>, ponieważ są one niezgodne typ. Zauważ też, że nie możesz zadeklarować swojej mapy w sposób jawny, jak powyżej, ponieważ nie możesz podać wielu granic dla symboli wieloznacznych. Tak właśnie kompilator określa typ.

dla tego rodzaju sytuacji, gdy kompilator nie może prawidłowo wywnioskować argumentów typu, jak to konieczne, można przekazać jednoznacznych argumentów typu, podczas wywołania metody, która jest, co zrobił w ostatnim Spróbuj:

ImmutableMap<Class<?>, String> map = ImmutableMap.<Class<?>, String>of( 
    Integer.class, "Integer", 
    Date.class, "Date" 
); 

to będzie teraz pracować, jako kompilator wie, z wyraźną typu argument, że wartość zwracana byłoby rodzaju - ImmutableMap<Class<?>, String>

tyle dziwnie, że d OES działać, jeśli mogę dodać obsady do Class<?> jednego (!) z kluczy

Jak tylko wpiszesz cast dowolnego elementu do Class<?>, ponieważ Class<?> oznacza rodzinie Wszystkie instancje Class<T>, a więc jest powszechnym typem nadrzędnym dla wszystkich instancji Class. Tak więc argument typu zostanie automatycznie wywiedziony jako Class<?>. I działałoby dobrze.

+0

Awesome. Dzięki! –

+0

@ IngoBürk Nie ma za co :) –