4

我目前正在为我正在学习的 Java 编程课程做家庭作业。我不是要一个确切的答案,而是要一些指导。

我正在处理的问题是:

我有一个实现过滤器接口的过滤器类。这个接口只有一种方法——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 。因此,尽管这些答案很棒,但我无法使用其中任何一个。

4

4 回答 4

4

使用的方法是Object.equals(Object)。您可以迭代集合fc并检查它是否包含 wich 元素equals(o)。如果是这样,请继续使用所述元素(类型为T)。

for(T e : fc) {
    if(o.equals(e)) {
        // carry on with e
    }
}

您可能还想覆盖o == null.

于 2013-05-15T18:53:45.243 回答
4

您的代码没有任何问题。

问题是由于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,它比依赖反射更健壮。

于 2013-05-15T17:38:08.533 回答
0

类型转换为T将起作用,您只需要小心捕获ClassCastExceptions,因为由于擦除,您无法在转换之前检查类型。

这会给你留下类似的东西:

public boolean contains(Object other) 
{
  try
  {   
    return currentFilter.matches((T)other) && fc.contains(other);
  }
  catch (ClassCastException e)
  {
    //Add some logging.
    return false;
  }
}

通常不赞成以这种方式使用异常,但在这种情况下,您别无选择。

于 2013-05-15T17:42:30.767 回答
0

由于 contains 方法使用了 .equals,而 .equals 是 Object 中的 qn 重写版本,我会考虑使用 Object 参数类型创建一个新的 Collection,例如 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);
}
于 2013-05-15T19:24:19.297 回答