我目前正在为我正在学习的Java编程课程做家庭作业。我不是要求一个确切的答案,而是要求一些指导。
我正在处理的问题是:
我有一个实现Filter接口的过滤器类。此界面只有一种方法 - matches(T element)
我已经配置了我的过滤方法来检查正在传入的整数的整数。
还有一个装饰器类,它装饰一个集合类,只显示通过过滤器的对象。
我在使contains(Object o)方法正常工作时遇到问题。
基本上contains(Obj o)
类中的FilteredCollection
方法应首先检查对象是否通过过滤器,如果是,则调用该对象上未修饰的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,并且我知道这将因为擦除而无法工作。
我已经做了大量的研究,而且我把它归结为需要使用反射。
我的教练给我的唯一提示是该对象只有一些我可以使用的方法,我应该使用其中一种。
感谢您的帮助!
编辑:项目的一个要求是不要在任何方法中将对象强制转换为T.因此,虽然这些答案很棒,但我无法使用其中任何一个。
答案 0 :(得分:4)
您的代码没有任何问题。
问题是由于java.util.Collection.contains(Object o)
接口方法不是通常输入的。这超出了您的控制范围。
选项1:简单方法
在您的方法实现中,您可以投射:
public boolean contains(Object o) {
return o != null && currentFilter.matches((T)o) && fc.contains(o);
}
选项2:将getParameterType()方法添加到Filter接口
此方法将返回在各个子类中实现的过滤器的泛型类型。
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,它比依赖反射更强大。
答案 1 :(得分:4)
使用的方法是Object.equals(Object)
。您可以迭代集合fc
并检查它是否包含equals(o)
元素。如果是这样,继续使用所述元素(类型为T
)。
for(T e : fc) {
if(o.equals(e)) {
// carry on with e
}
}
您可能还想涵盖o == null
。
答案 2 :(得分:0)
转换为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;
}
}
以这种方式使用异常通常是不受欢迎的,但在这种情况下,您没有太多选择。
答案 3 :(得分:0)
由于contains方法使用.equals,而.equals是对象中的一个重写版本,我会考虑使用Object参数类型创建一个新的Collection,所以例如ArrayList然后使用它的addAll方法来添加T类型集合中的所有内容 - 这将起作用,因为所有类都继承自Object。然后,当它使用equals方法时,它仍然是T类中的一个(假设T已覆盖它)。这可能不是一个完美的方法,但我认为它比检查对象是否可以转换为T更简单,更整洁。
public boolean contains(Object o) {
Collection<Object> temp = new ArrayList<Object>();
temp.addAll(fc);
return temp.contains(o);
}