Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java reflection: What does my Collection contain?

I've defined a method in a class:

public void setCollection(Collection<MyClass>);

and in another class

public void setCollection(Collection<OtherClass>);

(and really, lots of similar classes)

All are in classes with the same superclass, and I have a method in a support-class where I want to call this method and set it with items of the correct class type. Now, I can get that I'm setting a Collection by doing

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

But how do I figure out what class the objects in this collection should belong to?

Cheers

Nik

like image 753
niklassaers Avatar asked Nov 19 '09 16:11

niklassaers


2 Answers

Method.getGenericParameterTypes();

returns an array of Types which the parameter accepts. Complexity increases exponentially from there.

In your specific case, this would work:

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

But there are a lot of potential gotchas there, depending on how the parameter was declared. If it is a simple as in your example, great. If not, then there are a bunch of types you have to account for, both in the getGenericParameterTypes() method and in the getActualTypeArguments() method. It gets very hairy and ugly very fast.

like image 182
Yishai Avatar answered Oct 04 '22 23:10

Yishai


I would consider moving the setCollection methods out into their own classes and then doing something more like this (you can change it to create the whole collection at once instead of doing it as an element at a time).

This gets rid of reflection and ensures that it it typesafe at compile time. I could be misunderstanding what it is you are trying to do, but I think I got it. The "init" method would be the helper method (at least if I understand what you are aimng for).

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());
    }
}
like image 40
TofuBeer Avatar answered Oct 04 '22 22:10

TofuBeer