2013-03-27 13 views
6

Pochodzę ze świata C#, a ja właśnie dowiedziałem się o erasure w Javie, co nieco mnie podniosło. Czy naprawdę nie można odróżnić SomeGenericInstance<String> od środowiska wykonawczego SomeGenericInstance<Integer> w Javie?Czy w Javie nie można odróżnić podstawowego środowiska wykonawczego instancji?

Pytam, ponieważ wdrożyłem super prosty system sub-pub i chciałam mieć klasę ogólną GenericMessage<T>. Niezbędne jest wysyłanie GenericMessage<String> do słuchaczy GenericMessage<Integer>. Próbowałem go wdrożyć, korzystając z listy par klucz-wartość, w której kluczem jest obiekt Class reprezentujący typ komunikatu. Ale ta linia kodu daje true co jest problemem ...:

new GenericMessage<Integer>().getClass.equals(new GenericMessage<String>().getClass())

+3

Prawdopodobnie musisz zrobić coś takiego jak 'publishMessage (genericMessage, Integer.class);' – assylias

Odpowiedz

2

O ile mi wiadomo, przepraszam, to jest po prostu niemożliwe.

+0

Daję ci uprowadzenie, ponieważ wydaje się, że jest to jedyna PRAWDZIWA odpowiedź (i pierwszy raz to wskazałeś) :) – Nilzor

2

Możesz to zrobić, używając Java Reflection. Nie wiem, czy to zawsze dobry pomysł, ale na pewno jest to możliwe. Oto przykład:

public class Test{ 

    private List<String> list; 

    public static void main(String[] args) throws Exception{ 
     Field field = Test.class.getDeclaredField("list"); 
     Field f = field.getGenericType().getClass().getDeclaredField("actualTypeArguments"); 
     f.setAccessible(true); 
     Type[] genericTypes = (Type[]) f.get(field.getGenericType()); 
     System.out.println(genericTypes[0]); 
    } 

} 

Albo można oddać bezpośrednio do ParameterizedType, jeśli wydaje się lepiej do ciebie:

public class Test{ 

    private List<String> list; 

    public static void main(String[] args) throws Exception{ 
     Field field = Test.class.getDeclaredField("list"); 
     ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); 
     Type[] actualTypes = parameterizedType.getActualTypeArguments(); 
     System.out.println(actualTypes[0]); 
    } 

} 

Zarówno druk przykłady: class java.lang.String

Teraz wystarczy opuścić pełniejsze odpowiedź, to samo można zrobić dla mapy Mapa. Jak widać metoda getActualTypeArguments() zwraca Type[] i przez mapą, klucz typu byłby indeks 0, a typ wartość byłaby indeks 1. Przykład:

public class Test{ 

    private Map<String, Integer> map; 

    public static void main(String[] args) throws Exception{ 
     Field mapField = Test.class.getDeclaredField("map"); 
     ParameterizedType mapParameterizedType = (ParameterizedType) mapField.getGenericType(); 
     Type[] actualMapTypes = mapParameterizedType.getActualTypeArguments(); 
     System.out.println(actualMapTypes[0]); 
     System.out.println(actualMapTypes[1]); 
    } 

} 

Wydruki:

class java.lang.String 
class java.lang.Integer 
+0

Możesz faktycznie rzucić klasę na 'ParameterizedType', aby uzyskać faktyczne argumentyTypeArguments. '((ParameterizedType) field.getGenericType(). GetClass()). GetActualTypeArguments()' –

+0

Och, bardzo interesujące. Będę edytować odpowiedź, aby pokazać alternatywę przy użyciu tego podejścia. Dzięki! –

+0

Interesujące. Sądzę, że mogę użyć klasy Type lub łańcucha reprezentującego ją jako klucz w mojej HashMap. – Nilzor

2

można otworzyć go za pomocą następny trick:

public class Example<T> { 

    Class<T> genericType; 

    public Example(Class<T> genericType) { 
     this.genericType= genericType; 
    } 

    public static void main(String args[]) { 
     Example<Integer> ex1 = new Example<>(Integer.class); 
     Example<String> ex2 = new Example<>(String.class); 
     System.out.println(ex1.genericType); 
     System.out.println(ex2.genericType); 
    } 
} 

wyjściowa:

klasa java.lang.Integer

klasa java.lang.String

+0

Chociaż jest to prawdą, pomocne i może rozwiązać mój problem z PubSub-framwork, uważam, że nie odpowiada na moje pytanie, ponieważ dodaje warunek wstępny. – Nilzor

0

Nie jest możliwe odróżnienie "obiekt SomeGenericInstance<String>" oraz "obiekt SomeGenericInstance<String>", ponieważ nie ma różnicy . Jest tylko "obiekt SomeGenericInstance".

1

Oto sposób na uzyskanie tego, czego szukasz bez użycia refleksji (zakładając, że jesteś w stanie dokonać modyfikacji struktury pub-sub w celu przekazania tokena Class). Wielkie czubek kapelusza do assyli i zvzdhk za wskazanie mnie w kierunku literałów klasowych.

interface GenericMessage<T> { 

} 

interface StringMessage<T extends String> extends GenericMessage<T> { 
    String getString(); 
} 

interface IntMessage<T extends Integer> extends GenericMessage<T> { 
    int getInt(); 
} 

interface MessageListener<T> { 
    <T> void handleMessage(Class<T> type, GenericMessage<T> instance); 
} 

// "marker interfaces" 
interface StringMessageListener<T extends String> extends MessageListener<T> { 

} 

interface IntMessageListener<T extends Integer> extends MessageListener<T> { 

} 

class IntMessageImpl<T extends Integer> implements IntMessage<T> { 
    public int getInt() { 
     return 0; 
    } 
} 

class StringListenerImpl<T extends String> implements StringMessageListener<T> { 

    public <T> void handleMessage(Class<T> type, GenericMessage<T> genericMessage) { 
     StringMessage stringMessage = (StringMessage) genericMessage; // Typesafe cast since T extends String on both StringMessage and StringMessageListener 
     String message = stringMessage.getString(); 
     // Do something with message 
    } 
} 

class IntListenerImpl<T extends Integer> implements IntMessageListener<T> { 
     // an implementation for the Integer case ... 
} 

void showTypeChecking() { 

    GenericMessage<String> badStringMessage = new IntMessageImpl<>(); // Compile-time check fails due to bad type of new message implementation 

    MessageListener<Integer> badIntListener = new StringListenerImpl<>(); // Compile-time check fails due to bad type on new listener implementation 

    MessageListener<String> stringListener1 = new StringListenerImpl<>(); 
    MessageListener<String> stringListener2 = new StringListenerImpl<>(); 
    MessageListener<Integer> intListener = new IntListenerImpl<>(); 

    GenericMessage<String> stringMessage = new GenericMessage<String>() {}; 
    stringListener1.handleMessage(String.class, stringMessage); 
    stringListener1.handleMessage(Integer.class, stringMessage); // Compile-time check fails due to bad type on class literal 

    GenericMessage<Integer> intMessage = new GenericMessage<Integer>() {}; 
    intListener.handleMessage(Integer.class, intMessage); 

    GenericMessage<String> badIntMessage = new GenericMessage<String>() {}; 
    intListener.handleMessage(Integer.class, badIntMessage); // Compile-time check fails due to bad type on intMessage 

    GenericMessage uncheckedMessage = new IntMessageImpl(); 
    intListener.handleMessage(Integer.class, uncheckedMessage); // Compiler issues warning about unchecked assignment of uncheckedMessage argument 

    MessageListener uncheckedListener = new StringListenerImpl(); 
    uncheckedListener.handleMessage(String.class, stringMessage); // Compiler issues warning about an unchecked call to handleMessage() method 
} 

To nie jest bezpośrednio stosowane w tym przypadku, ale może okazać się dyskusja o typesafe heterogeneous container wzór pomocne w uczeniu się nieco więcej na temat leków generycznych Java. Jest to zdecydowanie jedna z trudniejszych części tego języka do opanowania.

Powiązane problemy