Search code examples
javagenericsreflection

call constructor, or get Class object, of generic parameter (or why doesn't it work?)


I'm just getting fancy with java for fun now.

Say I wanted to add an object to a collection; regardless of collection type. That would be easy by using generics, with T extends Collection. However, now lets say that I also want to handle the case where my collection is null by creating a new collection; but I want the collection returned to be the type that the calling method wants. So something like this:

public <T extends Collection<MyObject> addToCollection (Myobject object, T collection){
    if(collection==null)
        collection=new T();

    collection.add(object);

    return collection;
}

Obviously I can't call new T(), but is there an equivlant way of doing this? With reflection I could do it, but reflection assumes I know what class T is, and if collection is null I can't just call "collection.getClass()" My instinct is that this is impossible due to the compiler being unable to infer the type; only it seems as if it should, theoretically, be doable

If I mave a method that says soemthing like

HashSet<MyObject> set=addToCollection(object, null);

I can see at runtime that when I call this collection I expect a HashSet back, in theory I could therefore tell the method that what it type it expects T to be and therefore my method will now what collection to creaet. I strongly sspect Java doesn't do this; but if not is the reason for not implementing this capability due to it being too much of an edgecase that Sun/Oracle doesn't bother writing in a way to address it; or is there something about the way the compiler & runtime interact that makes this either impossible or extremly difficult?


Solution

  • You need to tell your method what type of collection to create if collection is null. For that, you could pass the class as a parameter.

    public <T extends Collection<String>> T addToCollection(String object, T collection, Class<T> clazz) {
    if (collection == null)
        collection = clazz.newInstance();
    
    collection.add(object);
    
    return collection;
    }
    

    EDIT

    As indicated in the comments, it is important to note that Class.newInstance() will only succeed if the constructor is has zero arguments and is already accessible. Otherwise, it is necessary to use Constructor.newInstance()