1

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?

dsollen
  • 6,046
  • 6
  • 43
  • 84
  • I'm not entirely sure what you're trying to do. Are you trying to cast a collection? –  Apr 10 '13 at 16:16
  • See http://stackoverflow.com/questions/9366121/calling-constructor-of-a-generic-type – JB Nizet Apr 10 '13 at 16:22
  • I was trying to write a method that would allow me to add a value to any collection; regardless of collection type. I then thought I would 'get fancy' and try initalizing the collection if need be. I don't really *need* to initialize the collection; I can always require them to pass a non-null value, but it got me thinking about how paramtization worked. I'm curious as to what is happening behind the scenes with the compiler when I use a generic parameter and why something Like what I asked wouldn't work. – dsollen Apr 10 '13 at 16:36
  • @JB the link you provided was relevant; but not exactly the same. In this case I know, for a fact, that I will be instantiating a collection and that *all* collection objects have a default constructor. So I know that the constructor I want exists; and that it could be called with reflection. The question is more does a Class type exist for the T that can be used to get to the constructor with reflection (and yes it's not worth getting that fancy for this method; it's a curiosity thing) – dsollen Apr 10 '13 at 16:38

1 Answers1

1

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()

Kal
  • 24,724
  • 7
  • 65
  • 65
  • 1
    Why not just pick a sane, default collection type? – Henrik Aasted Sørensen Apr 10 '13 at 16:22
  • @Henrik: because the method doesn't return a `Collection`, but a `T`. – JB Nizet Apr 10 '13 at 16:23
  • yes, that would definately work. However, I'm asking this more for my own interst, I was hoping to get more insight as to how the paramaterization actually occures and why I need to add the clazz argument – dsollen Apr 10 '13 at 16:34
  • @dsollen - The problem is that generics are subject to type erasure so at runtime there is no T. The link that JBNizet posted above talks more about generics and type erasure. – Kal Apr 10 '13 at 16:37
  • @Kal You could use `clazz.getConstructor().newInstance();` instead. It would worth to mention that the **default constructor must be defined and must be public**. – gaborsch Apr 10 '13 at 18:33