我目前正在做一门Java编程课程的家庭作业。我不是要一个确切的答案,而是要一些指导。
我正在解决的问题是:
我有一个过滤器类,它实现了一个filter接口。这个接口只有一个方法——matches(T element)
我已经配置了我的过滤器方法来检查传入的整数是否为素数。
还有一个修饰器类,它修饰一个集合类,使其只显示通过筛选的对象。
我在获取contains(Object o)方法正确工作时遇到问题。
基本上,FilteredCollection
类中的contains(Obj o)
方法应该首先检查对象是否通过过滤器,如果通过,则调用该对象上未修饰的contains()
方法。
假设我希望能够将FilteredCollection
类与许多不同类型的过滤器一起使用,我如何确定传入的对象类型,然后能够将该对象传递给正在实现的当前过滤器
这是我的PrimeNumberFilter
类:
public class PrimeNumberFilter implements Filter<Integer> {
public boolean matches(Integer e) {
int n = e.intValue();
if (n != 2 && n % 2 == 0) {
return false;
}
for (int i = 3; i * i <= n; i += 2) {
if (n % i == 0) {
return false;
}
}
return true;
}
}
然后这里是我的缩短FilteredCollection
类:
class FilteredCollection<T> implements Collection<T> {
Collection<T> fc;
Filter<T> currentFilter;
private FilteredCollection(Collection<T> coll, Filter<T> filter) {
this.fc = coll;
this.currentFilter = filter;
}
public static <T> FilteredCollection<T> decorate(Collection<T> coll,
Filter<T> filter) {
return new FilteredCollection<T>(coll, filter);
}
public boolean contains(Object o) {
//What do I do here?
return fc.contains(o);
}
传递给contains方法的对象必须通过过滤器,在本例中是PrimeNumberFilter
。
我得到的错误是它一直想要将对象强制转换为类型T,我知道这永远不会工作,因为擦除。
我做了大量的研究,我把它归结为需要使用反射。
我的导师给我的唯一提示是,对象只有几个方法可以使用,我应该使用其中一个。
谢谢你的帮助!
EDIT:项目的要求之一是不要在任何方法中将对象强制转换为T。所以,虽然这些答案很好,但我不能使用它们中的任何一个。
使用的方法是Object.equals(Object)
。您可以迭代集合fc
并检查它是否包含包含equals(o)
的元素。如果是,则继续处理上述元素(类型为T
)。
for(T e : fc) {
if(o.equals(e)) {
// carry on with e
}
}
您可能还想覆盖o == null
。
你的代码没有问题。
这个问题是由于java.util.Collection.contains(Object o)
接口方法不是泛型的。这是你无法控制的。
选项1:简单方法
在该方法的实现中,您可以强制转换:
public boolean contains(Object o) {
return o != null && currentFilter.matches((T)o) && fc.contains(o);
}
选项2:为Filter接口添加getParameterType()方法
此方法将返回在各种子类中实现的过滤器的泛型类型。
interface Filter<T> {
boolean matches(T parameter);
Class<T> getParameterType();
}
然后……
public boolean contains(Object o) {
return o != null && currentFilter.getParameterType().isAssignableFrom(o.getClass()) && currentFilter.matches((T)o) && fc.contains(o);
}
选项3:通过反射确定泛型类型
从技术上讲,过滤器的泛型类型实际上不会在运行时被擦除。类型擦除在这里不适用,因为PrimeNumberFilter是一个实际的类,它实现了一个泛型接口。@SuppressWarnings("unchecked")
public boolean contains(Object o) {
Class<?> genericTypeOfFilter = getGenericTypeOfFilter(currentFilter);
return o != null && genericTypeOfFilter.isAssignableFrom(o.getClass()) && currentFilter.matches((T)o) && fc.contains(o);
}
static <T> Class<T> getGenericTypeOfFilter(Filter<T> filter) {
try {
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
Class<T> type = (Class<T>) ((ParameterizedType)filter.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
return type;
}
catch (Exception e) {
throw new IllegalStateException("Unexpectedly failed to read generic type of filter: " + filter, e);
}
}
如果是我的代码,在这种情况下我会选择选项2,它比依赖反射更健壮。
因为contains方法使用。equals,而。equals是Object中一个的qn重写版本,我会考虑用Object参数类型创建一个新的集合,所以例如ArrayList然后使用它的addAll方法来添加来自T类型集合的所有内容-这将起作用,因为所有类都继承自Object。然后,当它使用equals方法时,它仍然是来自类T的那个(假设T已经覆盖了它)。这可能不是完美的方法,但我认为它比尝试检查Object是否可以强制转换为t更简单、更简洁。
public boolean contains(Object o) {
Collection<Object> temp = new ArrayList<Object>();
temp.addAll(fc);
return temp.contains(o);
}
将类型转换为T
将工作,您只需要小心捕获ClassCastException
s,因为由于擦除,您无法在转换之前检查类型。
这将给你留下这样的内容:
public boolean contains(Object other)
{
try
{
return currentFilter.matches((T)other) && fc.contains(other);
}
catch (ClassCastException e)
{
//Add some logging.
return false;
}
}
以这种方式使用异常通常是不受欢迎的,但在这种情况下,您没有太多选择。