2012-07-19 11 views
7

Próbuję nawiązać połączenie metody takie jak to,Jak uzyskać klasę z typów generycznych w języku Java

public class GenericsTest<T> { 

    public static <T> Map<String, T> createMap(Class<? extends Map<String, T>> clazz) { 
     return null; 
    } 

    public static void main(String[] argv) { 
     Map<String, Integer> result = createMap(TreeMap.class); 
    } 
} 

ale otrzymuję ten błąd,

<T>createMap(java.lang.Class<? extends java.util.Map<java.lang.String,T>>) in test.GenericsTest<T> cannot be applied to (java.lang.Class<java.util.TreeMap>) 

Jak rozwiązać ten problem?

+0

Nie można z powodu typu wymazać. W szczególności podpis metody nie ma sensu, ponieważ próbujesz uzyskać 'T', nawet jeśli bierzesz' Class' dla 'Map' z parametrem value jako' T'. Nigdy nie będziesz w stanie uzyskać typu zagnieżdżonego bez istniejącej instancji obiektu. (Mógłbyś przekazać '(nowy HashMap ()). GetClass()', ale nadal nie będziesz w stanie odzyskać 'Integer' z tej metody). – pickypg

+0

Funkcja createMap() pochodzi z biblioteki, której używamy. Musi istnieć sposób na wywołanie tej metody. –

+0

Pytam o zasadność zwróconego obiektu. Przypuszczalnie zwraca instancję parametru value, 'T', kiedy jest przekazywana w' Class' dla 'Map', a nazwa metody to' createMap'. Nic z tego nie ma sensu. – pickypg

Odpowiedz

3
Map<String, Integer> instance = new TreeMap<String, Integer>(); 

@SuppressWarnings("unchecked") 
Map<String, Integer> map = 
    createMap((Class<? extends Map<String, Integer>>)instance.getClass()); 

map.put("x", 1); 

System.out.println("THIS IS x: " + map.get("x")); 

Będzie to odpowiednio wydrukować 1. Wykonanie metody jest najprawdopodobniej

try 
{ 
    return clazz.newInstance(); 
} 
catch (Exception e) 
{ 
    throw new RuntimeException(e); 
} 

Lepsze wdrażanie ich API byłoby dla nich, aby poprosić o rodzaju, T, a dla im odsyłać wybrany przez siebie numer Map zamiast prosić o wszystkie szczegóły. W przeciwnym razie, o ile nie są one wypełnieniu Map z wszelkimi danymi, można instancji Map z uniwersalnym typem argumentu sobie tak:

public static <T> Map<String, T> getMap() 
{ 
    return new TreeMap<String, T>(); 
} 

Następnie można uzyskać dostęp, który bez ostrzeżenia:

// note the lack of type arguments, which are inferred 
Map<String, Integer> instance = getMap(); 

// alternatively, you could do it more explicitly: 
// Map<String, Integer> instance = ClassName.<Integer>getMap(); 

tam naprawdę nie ma powodu, dla nich prosić o rodzaju telefonu Map wyjątkiem Class aby oddać dokładne dopasowanie do wykonania (np jeśli trzymać w HashMap, wtedy wrócić do HashMap, a jeśli trzymasz się w TreeMap, a następnie otrzymasz numer TreeMap). Jednak podejrzewam, że TreeMap straci wszystkie Comparator, z których został zbudowany, a ponieważ jest to niezmienne (final) pole TreeMap, to nie można tego naprawić; oznacza to, że Map nie jest taki sam w tym przypadku, ani nie jest prawdopodobne, że jest to, co chcesz.

Jeśli wypełniają dane Map, ma to jeszcze mniejszy sens. Zawsze można przekazać wystąpienie Map do wypełnienia lub poprosić o zwrócenie Map, który można po prostu zawinąć (np. new TreeMap<String, Integer>(instance);), i powinni wiedzieć, który z nich oferuje najwięcej użyteczności dla danych.

+0

To działa, ale z niezaznaczonym ostrzeżeniem. Musi być sposób na wywołanie tej metody bez ostrzeżenia. Taki jest cel używania leków generycznych. –

+0

Masz rację co do celu, ale Java ma ograniczone rodzaje generyczne z powodu typu Erasure, który powoduje ten problem; istnieją ograniczenia co do tego, co możesz zrobić bez zmuszania kompilatora do robienia tego za ciebie. Jest to prawdopodobnie moja najmniej ulubiona funkcja Javy, ponieważ najtrudniej będzie je naprawić lub wymienić. .NET ma znacznie bardziej wszechstronny system generyczny, ponieważ nie są one zapomniane w czasie wykonywania. Po kompilacji, ten dostęp do mapy stanie się '(Integer) map.get (" x ")', ponieważ zwracany typ jest w rzeczywistości 'Obiektem'. – pickypg

Powiązane problemy