检查通用类型

时间:2011-07-09 07:09:53

标签: java generics type-erasure

Java Collections接口(例如,ListSet)定义contains方法以接受任何Object。

 public boolean contains(Object o)

但是,当涉及到实现此方法时,我正在处理的特定集合要求我有一个类与类的泛型类型E兼容(即要么是E类,要么E的子类,或者如果E是接口则实现E的类)。换句话说,如果o可以转换为E类型,那么它是兼容的。这提出了一个问题,因为Java会删除Generic类型信息,因此不可能这样:

public boolean contains(Object o)
{
    if(o instanceof E) // compile error due to type erasures
    {
        // ... check if this collection contains o
    }
    return false;
}

我的问题是完成这样的事情的最佳方法是什么? Oracle Article on Type Erasures提到这是禁止的,但没有提供任何解决方案。

我只能想到一种半优雅的方式来解决这个问题:

进行转换以输入E.如果转换失败,则o不能是E类型或E类的子类。

public boolean contains(Object o)
{
    try
    {
        E key = (E) o; // I know it's unsafe, but if o is castable to E then the method should work
        // check if this collection contains key
    }
    catch(ClassCastException e)
    {
        // invalid type, cannot contain o
    }
    return false;
}

虽然这样可行,但看起来很混乱(我不喜欢以这种方式使用Exceptions)。

是否有更好的方法来实现同一目标(不允许更改方法签名)?

编辑:是的,这不起作用,因为E被删除为对象:(

2 个答案:

答案 0 :(得分:5)

这只能通过(1)传入预期的Class或(2)检查定义<E>的某些反射元素的泛型类型参数来实现。让我详细说明一下。

最常见的情况是只需要调用者传入E的运行时类。

public class MyClass<E> {
    private final Class<E> realType;
    public MyClass(Class<E> realType) {
        this.realType = realType;
    }
    public boolean Contains(Object o) {
        E e = realType.cast(o); // runtime cast - will throw ClassCastException.
        // Could also use realType.isInstance(o)
        // or realType.isAssignableFrom(o.getClass())
        ...
    }
}

呼叫者:

new MyClass<MyObject>(MyObject.class)

这通常是类型安全的,因为编译器将验证<E>是否匹配。当然调用者可以绕过编译器的检查...你无能为力!

对于(2),我的意思是你可以使用反射来检查静态泛型类型参数。在您的情况下,这可能不是一个好的选择,因为您必须有权访问静态定义<E>的某些字段,方法或超类声明。最常见的方法是使您的类抽象并让调用者扩展它。 Hamcrest的TypeSafeMatcher(见ReflectiveTypeFinder)使用了这种方法,效果很好。 (请注意,TypeSafeMatcher基本上只是让程序员更容易使用选项(1);它仍然提供了一个构造函数,当反射不起作用时,它会获取类的情况!)如果你想要真正的幻想,你可以检查{{1 }}。这并不像听起来那么容易 - 请参阅this good article。我甚至不确定该文章是否涵盖了所有边缘情况 - 您基本上是重新实现了编译器!所以只需使用选项(1)并且很高兴你没有在C#中使用泛型: - )

答案 1 :(得分:0)

我不明白为什么你以这种方式弄乱.contains()contains()Collection的规范表明,如果给定对象等于容器的某个元素(在.equals()意义上),则返回true。那么你是否正在改变.contains()的含义?你的平等如何定义?