如何将未知类型的对象传递给未知类型的类



我目前正在做一门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;
  }
}

以这种方式使用异常通常是不受欢迎的,但在这种情况下,您没有太多选择。

相关内容

  • 没有找到相关文章

最新更新