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

时间:2013-05-15 17:08:56

标签: java generics reflection

我目前正在为我正在学习的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.因此,虽然这些答案很棒,但我无法使用其中任何一个。

4 个答案:

答案 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);
}