Java反射:我的Collection包含什么?

时间:2009-11-19 16:25:27

标签: java generics reflection

我在类中定义了一个方法:

public void setCollection(Collection<MyClass>);

并在另一个班级

public void setCollection(Collection<OtherClass>);

(实际上,很多类似的课程)

所有都在具有相同超类的类中,并且我在支持类中有一个方法,我想调用此方法并使用正确类类型的项设置它。现在,我可以通过执行

来设置集合
Method setter = ...;
Class<?> paramClass = setter.getParameterTypes()[0]; // Is Collection in this case
if(paramClass.equals(Collection.class)) {
  HashSet col = new HashSet();
  // fill the set with something
  setter.invoke(this, col);
}

但是如何确定此集合中的对象应该属于哪个类?

干杯

的Nik

5 个答案:

答案 0 :(得分:9)

Method.getGenericParameterTypes();

返回参数接受的Types数组。复杂性从那里呈指数增长。

在您的具体情况下,这可行:

    Method m = Something.class.getMethod("setCollection", Collection.class);
    Class<?> parameter = (Class<?>) ((ParameterizedType) m.getGenericParameterTypes()[0]).getActualTypeArguments()[0];

但是那里存在很多潜在的问题,具体取决于参数的声明方式。如果它像你的例子一样简单,那很好。如果没有,那么在getGenericParameterTypes()方法和getActualTypeArguments()方法中都有一些类型需要考虑。它变得非常毛茸茸,非常快。

答案 1 :(得分:2)

我会考虑将setCollection方法移到他们自己的类中,然后做更像这样的事情(你可以改变它来一次创建整个集合,而不是一次作为一个元素)。

这消除了反射并确保它在编译时是类型安全的。我可能会误解你要做的是什么,但我想我明白了。 “init”方法将是辅助方法(至少如果我理解你的目标是什么)。

public class Main 
{
    public static void main(String[] args) 
    {
        List<Car> car;
        List<Bar> bar;

        car = new ArrayList<Car>();
        bar = new ArrayList<Bar>();
        init(car, new CarCreator());
        init(bar, new BarCreator());
    }

    private static <T> void init(final List<T>    list,
                                 final Creator<T> creator)
    { 
        for(int i = 0; i < 10; i++)
        {
            final T instance;

            instance = creator.newInstance(i);
            list.add(instance);
        }
    }
}

interface Foo
{    
}

class Bar implements Foo 
{ 
}

class Car implements Foo 
{ 
}

interface Creator<T>
{
    T newInstance(int i);
}

class BarCreator
    implements Creator<Bar>
{
    public Bar newInstance(final int i)
    {
        return (new Bar());
    }
}

class CarCreator
    implements Creator<Car>
{
    public Car newInstance(final int i)
    {
        return (new Car());
    }
}

答案 2 :(得分:1)

Java使用类型擦除实现泛型。编译器仅将此类型信息用于安全检查,字节代码不包含有关泛型类型的任何信息。因此,运行时也不知道它,因此您无法通过反射来检索它。

您可以找到一些其他信息here

<强>更新

要纠正自己,请考虑以下方法:

public <T> String doSomething(T first, int second) {
    return first.toString();
}

在字节代码中,这将具有以下签名(注意T类型):

<T:Ljava/lang/Object;>(TT;I)Ljava/lang/String

所以我上面所说的只有这个例外。

答案 3 :(得分:0)

你似乎已经知道了二传手。所以在我看来,在这种情况下,MyClass和OtherClass(不是集合,但是项目)共享一些类似的界面。 在这种情况下,您实际上并不需要知道确切的类型。

答案 4 :(得分:0)

正如candiru已经解释的那样,'Collection Type'被删除了,在运行时它只是一个对象类型集合。

如果允许您将子类中的消息签名稍微更改为

public void setCollection(Collection<T> aCollection, Class<T> elementType);

然后您可以传递第二个参数来保存“集合类型”的信息,用法如下:

public void setCollection(Collection<MyClass> myClassCollection, 
                          Class<MyClass> clazz);

并使用它:

Collection<MyClass> myClassCollection = new ArrayList<MyClassCollection>();
setCollection(myClassCollection, MyClass.class);

但另一方面 - 通常你不必关心它,因为类型在运行时被删除,你可以把任何类型的对象放在paramterized集合中,只要你没有被编译器捕获;)