3

I have two variables

Collection<Service> services = new ArrayList<Service>();
Collection<Subscription> subscriptions = new ArrayList<Subscription>();

and I have the following method, I was wondering how can I find the value of "?" in this method, or how can I find if services was passed or subscriptions was passed?

myMethod(Collection<?> myCollection) {

   if (myCollection is of type Service) {
      // process service
   }
   else if (myCollection is of type Subscription) {
      // process subscription
   }
}

Thanks.

Rizwan
  • 97
  • 1
  • 12
  • 1
    If you need knowledge of the type, you shouldn't be using a wildcard. Just have separate methods for each type of collection. – dlev Aug 29 '11 at 17:06
  • I have to use the same method name aka overloading, but at compile time myMethod(Collection) and myMethod(Collection) will be considered as duplicate methods, hence I was trying to use if/else in one method. – Rizwan Aug 29 '11 at 18:52
  • You're right; I forgot about type-erasure. – dlev Aug 29 '11 at 18:53

4 Answers4

4

You cannot. Java has erasure-based generics, which means that the type parameters are not available at runtime.

If your collection is not empty, you can do an instanceof on the first element or something, of course.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • Thanks for the reply, I also thought that doing the instanceof on first element would help but I was wondering if there is another way but looks like there isn't. – Rizwan Aug 29 '11 at 18:40
2

Using that if-else construct in a generic method defeats the purpose of generics. If you need to know the type of what is being passed in at runtime, it really shouldn't be generic and you should write a separate method for each type.

Chad La Guardia
  • 5,088
  • 4
  • 24
  • 35
  • I agree, it is a bad design, but I am not the designer, the design was given to me and I can't change it. The design was done by our "design team"... – Rizwan Aug 29 '11 at 18:42
2

You can't (except by using reflection), since the generic type parameter gets erased during compilation.

And I would recommend you rethink your design rather than trying to solve it with generics. Passing arbitrary types of collections into the same method is a recipe for problems in the long run.

Community
  • 1
  • 1
Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • 1
    Even reflection can't get at the type parameter; like you say, it's erased already! – C. K. Young Aug 29 '11 at 17:09
  • @Chris, I couldn't find the original post I saw about this, but found another one, see the link in my answer. – Péter Török Aug 29 '11 at 17:21
  • Re edit: that's to get at the type of a _parameter_, not of the passed-in object. Alas, the type parameters of actual live objects are actually erased. – C. K. Young Aug 29 '11 at 17:21
  • @Chris, in my understanding this is what the OP is after. At any rate, this is an academic question, because it certainly is a bad idea :-) – Péter Török Aug 29 '11 at 17:24
1

There is no Java way of doing exactly that. Type erasure gets in the way. And if you need to do so, it is also probably a design issue. But if you must, there is horrible way to do almost that:

Change your variables to

Collection<Service> services = new ArrayList<Service>(){};
Collection<Subscription> subscriptions = new ArrayList<Subscription>(){};

Note the {} after the (). You are not creating an ArrayList, but a anonymous class that inherits from ArrayList. And type erasure does not apply there. So you can tell the real type by doing something like

private static Class<?> detectType(Collection<?> col) {
    return (Class<?>) ((ParameterizedType) col.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}

That method will return the actual class. It does work, it is disgusting. It is up to you.

Pablo Grisafi
  • 5,039
  • 1
  • 19
  • 29