Java Collections接口(例如,List
或Set
)定义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被删除为对象:(
答案 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()
的含义?你的平等如何定义?