6

我来自 C# 世界,我刚刚了解了 Java 中的擦除,这让我有点反感。Java中真的没有办法与运行时区分开SomeGenericInstance<String>来吗?SomeGenericInstance<Integer>

我问是因为我已经实现了一个超级简单的 pub-sub 框架,并且我想要一个通用类GenericMessage<T>。重要的是不要发送GenericMessage<String>GenericMessage<Integer>. 我尝试通过一个键值对列表来实现它,其中键是Class表示消息类型的对象。但是这个代码行产生true了一个问题......:

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

4

5 回答 5

2

据我所知,对不起,这根本不可能。

于 2013-03-27T13:42:20.303 回答
2

您可以使用Java Reflection来做到这一点。不知道这是否总是一个好主意,但这肯定是可能的。这是一个例子:

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]);
    }

}

或者,您可以直接转换为 ParameterizedType,如果您觉得更好的话:

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]);
    }

}

两个示例都打印:class java.lang.String

现在只是为了留下更完整的答案,对Map也可以这样做。如您所见,该getActualTypeArguments()方法返回 aType[]并且对于Map,键类型为索引 0,值类型为索引 1。示例:

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]);
    }

}

印刷:

class java.lang.String
class java.lang.Integer
于 2013-03-27T13:46:05.180 回答
2

您可以使用下一个技巧访问它:

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);
    }
}

输出:

类 java.lang.Integer

类 java.lang.String

于 2013-03-27T13:47:43.633 回答
1

这是一种在不使用反射的情况下获得所需内容的方法(假设您能够对 pub-sub 框架进行修改以传递Class令牌)。向 assylias 和 zvzdhk 致敬,因为他们为我指明了类文字的方向。

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
}

它在这种情况下并不直接适用,但您可能会发现对类型安全异构容器模式的讨论有助于更多地了解 Java 泛型。这绝对是语言中最难掌握的部分之一。

于 2013-03-28T05:34:48.820 回答
0

无法区分“一个SomeGenericInstance<String>对象”和“一个SomeGenericInstance<String>对象”,因为没有区别。只有“一个SomeGenericInstance对象”。

于 2013-03-28T04:50:01.833 回答