34

可能重复:
运行时局部变量的泛型类型

我是 Java 泛型的新手,来自 .NET 世界,我习惯于编写这样的方法:

public void genericMethod<T>(T genericObject)
{
    if (genericObject is IList<String>)
    {
        //Do something...
    }            
}

该方法接受泛型类型的对象,并检查该对象是否实现了泛型接口的特定IList<>版本,在本例中为IList<String>

现在,在 Java 中,我可以做到这一点:

public <T> void genericMethod(T genericObject)
{
    if (genericObject instanceof Set<?>)
    {
       //Do something...
    }
}

Java不让我做if (genericObject instanceof Set<String>)

据我所知,由于类型擦除,通常在 Java 中,这将由类对象处理,我们将执行以下操作:

public <T> void genericMethod(T genericObject)
{
    Class<OurTestingType> testClass = OurTestingType.class;
    if (genericObject.getClass() == testClass)
    {
       //Do something...
    }
}

但由于我要检查的类型是通用接口,所以你不能这样做:

Class<Set<String>> testClass = Set<String>.class

那么,如何在 Java 中检查泛型对象是否实现了特定类型的Set<String>?

4

2 回答 2

24

Java 实现了擦除,因此无法在运行时判断它是否genericObject是一个实例Set<String>。保证这一点的唯一方法是在泛型上使用边界,或者检查集合中的所有元素。

编译时通用边界

使用边界检查,将在编译时检查:

public <T extends SomeInterface> void genericMethod(Set<? extends T> tSet) {
    // Do something with tSet here
}

爪哇 8

我们可以使用 Java 8 中的流在一行中本地完成此操作:

public <T> void genericMethod(T t) {
    if (t instanceof Set<?>) {
        Set<?> set = (Set<?>) t;
        if (set.stream().allMatch(String.class:isInstance)) {
            Set<String> strs = (Set<String>) set;
            // Do something with strs here
        }
    }
}

Java 7 及更早版本

对于 Java 7 及更早版本,我们需要使用迭代和类型检查:

public <T> void genericMethod(T t) {
    Set<String> strs = new HashSet<String>();
    Set<?> tAsSet;
    if (t instanceof Set<?>) {
        tAsSet = (Set<?>) t;
        for (Object obj : tAsSet) {
            if (obj instanceof String) {
                strs.add((String) obj);
            }
        }
        // Do something with strs here
    } else {
        // Throw an exception or log a warning or something.
    }
}

番石榴

根据下面的 Mark Peters 的评论,如果您可以将 Guava 添加到您的项目中,Guava 也有为您执行此操作的方法:

public <T> void genericMethod(T t) {
    if (t instanceof Set<?>) {
        Set<?> set = (Set<?>) t;
        if (Iterables.all(set, Predicates.instanceOf(String.class))) {
            Set<String> strs = (Set<String>) set;
            // Do something with strs here
        }
    }
}

声明,Iterables.all(set, Predicates.instanceOf(String.class))本质上与set instanceof Set<String>.

于 2012-09-20T15:31:28.333 回答
11

遗憾的是,您在 Java 中没有该选项。在 Java 中, a和 a之间没有运行时差异。是编译器确保您永远不会使用. 即使编译器的强制执行并不严格,所以你可以“合法地”用未经检查的强制转换来做这种可憎的事情......List<String>List<Integer>add()IntegerList<String>

总而言之,对于(几乎)任何关于运行时类型识别的问题,您必须List<String>了解它的实际情况:只是一个原始的List. 这称为类型擦除

也就是说,没有什么能阻止您检查 a 的内容以List了解它们的类型:

public boolean isListOf(List<?> list, Class<?> c) {
    for (Object o : list) {
        if (!c.isInstance(o)) return false;
    }

    return true;
}

要使用此方法:

    // ...
    if (genericObject instanceof List<?>) {
        if (isListOf((List<?>) genericObject, String.class)) {
            @SuppressWarnings("unchecked")
            List<String> strings = (List<String>) genericObject;
        }
    }

An interesting observation: if the list is empty, the method returns true for all given types. Actually there is no runtime difference between an empty List<String> and an empty List<Integer> whatsoever.

于 2012-09-20T16:39:52.087 回答