2012-02-02 13 views
9

Zakładając deklarację typu rodzajowego (Java)Jak zbudować obiekt typu Java w środowisku wykonawczym z ogólnej definicji typu i parametrów typu środowiska wykonawczego?

class Foo<T> { 
    public T bar; 
} 

jak mogę, w czasie wykonywania, instancję typu obiektu, który reprezentuje Foo parametryzowane ciągu określonego typu T (znany także tylko przy starcie)?

+1

Dlaczego go potrzebujesz? Typ erasure sprawia, że ​​idea jest bez sensu, więc spróbuj wyjaśnić, co próbujesz osiągnąć. – Viruzzo

+0

Potrzebuję go, aby udostępnić bibliotekę serializacji (de) serializacji (google gson w tym przypadku) obiektu typu reprezentującego moje oczekiwane typy podczas deserializacji z łańcucha JSON. Wymazanie nie ma sensu, ponieważ obiekt typu zostanie sprawdzony w czasie wykonywania. – SoftMemes

+0

"Co jest potrzebne dla biblioteki, której używam, aby zobaczyć pole jako typ podany w czasie wykonywania", "obiekt typu reprezentujący moje spodziewane typy", co to znaczy? Czy możesz rozwinąć to, co próbujesz zrobić? – Viruzzo

Odpowiedz

12

Myślę, że rozumiem twoje pytanie. Chcesz serializować Foo<T> i masz obiekt klasy T w środowisku wykonawczym (ale nie jest on naprawiony w czasie kompilacji). Dlatego sugerowane rozwiązanie w Gson tworzenia anonimowej podklasy TypeToken nie działa, ponieważ wymaga to, aby sparametryzowany typ (na przykład Foo<String>) był zakodowany w czasie kompilacji i nie zadziała, jeśli użyjesz czegoś takiego jak Foo<T>.

Zobaczmy jednak, co naprawdę osiąga metodana stronie Gson. Tworzysz obiekt anonimowej podklasy TypeToken, a następnie pytasz o jego parametr typu, używając metody getType(). Nadklasa klasy jest częścią jej metadanych i zawiera ogólne parametry jej nadklasy. Tak więc w czasie wykonywania może on przyjrzeć się własnej hierarchii dziedziczenia i dowiedzieć się, jaki typ parametru został użyty dla TypeToken, a następnie zwraca instancję java.lang.reflect.Type dla tego typu (która, jeśli jest sparametryzowana, będzie instancją ParameterizedType). Po uzyskaniu tej instancji Type, należy przekazać ją jako drugi argument toGson().

Wszystko, co musimy zrobić, to znaleźć inny sposób na utworzenie tego wystąpienia: ParameterizedType. ParameterizedType to interfejs, ale niestety, publiczne API Java nie zapewnia żadnych konkretnych implementacji ani żadnego sposobu dynamicznego tworzenia ParameterizedType. Wygląda na to, że istnieje klasa o nazwie ParameterizedTypeImpl, w prywatnych Sun API iw kodzie Gsona, którego możesz użyć (e.g. here). Możesz po prostu skopiować kod i zmienić jego nazwę na własną klasę.Następnie, aby utworzyć obiekt Type reprezentujący Foo<String> w środowisku wykonawczym, można po prostu zrobić coś takiego, jak new ParameterizedTypeImpl(Foo.class, new Type[]{String.class}, null) (untested)

+0

Najbardziej niesamowite, wydajesz się być jedynym, który to dostanie i masz konkretne rozwiązanie. Jestem zaskoczony, że nie ma "oficjalnego" sposobu na zrobienie tego, ale ta metoda powinna zdecydowanie wystarczyć, aby grać z Gsonem, co jest wszystkim, czego potrzebuję. Dziękuję Ci! – SoftMemes

+0

Cóż, jest prosty powód, dla którego ... to nie jest to, o co prosiłeś. Jest to wskazówka dla Gson, jak parsować, a nie metoda tworzenia typu. –

+0

Działa jak urok. Wielkie dzięki. – Abhi

2

Zwykłym sposobem wokół typu skasowaniem jest:

class Foo<T> { 

    Class<T> clazz; 

    public Foo(Class<T> c) { 
     clazz = c; 
    } 

    public T bar { 
     return clazz.newInstance(); 
    } 
} 

Jeśli nie ma no-args konstruktor dla T, można zrobić coś bardziej wyszukane za pomocą refleksji na obiekt klasy; gdy masz instancję Class<T>, możesz uzyskać instancję.

Stawiłem czoła dokładnie temu problemowi z gsonem. Skończyło się tak:

public class JsonWrapper { 
    private String className; 
    private String json; // the output of the gson.toJson() method 
} 

I kiedy potrzebne do deserialise, zrobiłem Class.forName(className) wtedy miałem wszystko, co potrzebne do wywołania metody fromJson() z biblioteki gson.

Nie mogłem uwierzyć, że gson nie wspiera tego w sposób natywnie - wydaje się, że tak oczywiste jest, aby chcieć zrobić ... uzyskać json i zmienić go w obiekt bez wiedząc, która klasa jest wcześniej.

+0

Chociaż nie potrzebuję po prostu tworzyć wystąpienia określonego w czasie wykonywania, muszę utworzyć obiekt Type, który zachowa informacje o typie. Jak zauważył Jon, informacje o typie nie zawsze są tracone. Jednym z możliwych sposobów byłoby stworzenie nowego typu dynamicznie podklasy mojego rodzaju generycznego z jego parametrami? Właściwie nie potrzebuję go do reprezentowania Foo , o ile można go przypisać do Foo ... – SoftMemes

+0

Co do twojego przykładu z Gsonem, to także moje obejście, ale byłoby lepiej, gdyby można było tego uniknąć. Mój przypadek jest nieco inny, ponieważ znam oczekiwany typ przed deserializacją, ale buduję ogólną klasę bazową, aby poradzić sobie z komunikatami dzielącymi pewną ogólną strukturę, ale różniących się układem ładowności ... – SoftMemes

-1

Gson ma faktycznie zapewnić rozwiązanie tego problemu: https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener

Oczywiście co to robi to trochę więcej niż w roztworze czeska (trzeba jeszcze przekazać parametr typu jakoś), ale odbywa się automatycznie.

+0

Mam sen to, ale nadal zakłada, że ​​znasz typ w czasie kompilacji, co nie jest dla mnie. Aby utworzyć instancję "TypeToken", muszę utworzyć instancję typu generic z parametrem typu w czasie wykonywania, i jak to zrobić, to jest moje oryginalne pytanie ... – SoftMemes

+0

Do tego potrzebujesz rozwiązania Bohemian do używania klasy "Class" element inicjowany w konstruktorze (lub równoważny "String"). Nie ma czystej drogi wokół wymazania, wszystkie inne, które widziałem, są znacznie bardziej niespokojne. – Viruzzo

+0

Tak więc, mam obiekt klasy reprezentujący rzeczywisty typ T, ale to wciąż nie wystarczy, aby powiedzieć gsonowi, że oczekuję, że nazwany atrybut będzie tego typu. – SoftMemes

-1

Co wszyscy mówili :). Klasa, którą chcesz utworzyć, musi być dostępna w środowisku wykonawczym. Wiele sposobów na to: umieść nazwę klasy lub klasy w zmiennej lokalnej w swojej fabryce, zastosuj metodę chronioną, utwórz klasę "obiektów fabrycznych", jeśli potrzebujesz tego w wielu różnych miejscach itd. Jest to rodzaj zadanie, które wykonują frameworki, więc jeśli używasz jednego z nich, można to zrobić, konfigurując to.

+0

Nie, to wcale nie odpowiada na moje pytanie. Mam obiekt klasy dla argumentu typu, nie jest to kwestia uzyskania klasy argumentu typu ogólnego w czasie wykonywania, jest to kwestia tworzenia instancji typu Typ w środowisku wykonawczym. – SoftMemes

-1

Wydawało się, że naprawdę prostsze niż większość ludzi myśli:

Type collectionType = new TypeToken<ArrayList<Person>>(){}.getType(); 
ArrayList<Person> persons = gson.fromJson(response, collectionType); 

Nie trzeba kopiować klasę ParameterizedTypeImpl jak newacct sugerowane.

+1

-1 gdybym mógł; Freed wyraźnie powiedział, że T jest znane tylko w czasie wykonywania. W związku z tym to rozwiązanie nie działa dla niego, jak nowy sposób wyjaśniono w jego odpowiedzi. –

4

Załóżmy, że mówimy na przykład o List<String>.
Można skonstruować coś takiego:

Type type = new ParameterizedType() { 
      public Type getRawType() { 
       return List.class; 
      } 

      public Type getOwnerType() { 
       return null; 
      } 

      public Type[] getActualTypeArguments() { 
       return new Type[] { String.class}; 
      } 
     }; 

Jeśli to potrzebne dla de-szeregowania z JSON, można użyć tej Type z wywołaniem gson.fromJson

+0

Jedyne rozwiązanie, które działało dla mnie, zamiast "String.class" Mam swój "clazz", którego parametr jest końcowym parametrem typu "Class ". – yonilevy

Powiązane problemy