views:

267

answers:

6

The title basically says it all: if I have a java method that is generic in T, can I find out anything about T? In particular, can I check whether T implements a certain interface or extends a certain class?

I would like to do something like

public <T> List<T> doSth(List<T> l) {

  if(T extends Comparable) {
    // do one thing
  } else {
    // do another
  }

  return l;
}

Any hints?

Thanks a lot,

Johannes

+3  A: 

No - due to type erasure. At execution time, you don't know the type of T at all.

One option would be to specify the type as another parameter:

public <T> List<T> doSth(List<T> l, Class<T> clazz) {
    if (Comparable.class.isAssignableFrom(clazz)) {
        ...
    }
}
Jon Skeet
Ergh. Why bring `Class` into this??
Tom Hawtin - tackline
@Tom: Because it answers the question? I know having to supply the class is icky, but it does the job...
Jon Skeet
+1  A: 

yes, you can:

public <T> List<T> doSth(List<T> l) {
  //You could also check every element, if there is a chance only some will be comparable
  if (l.size() >0 && l.get(0) instanceof Comparable) {
    // do one thing
  } else {
    // do another
  }

  return l;
}

Note that you are checking what type the elements in "l" are, NOT T - that is the key.

Edit: Changed the code to handle the fact that it was a list - I had missed that in my original reading.

aperkins
That's wrong for two reasons. 'l' is List so it's not going to be Comparable and it's not what OP wants.
ChssPly76
-1: You missed l beeing of type List<T> and not of type <T>
tangens
@tangens - you are correct, I have corrected my solution for the list problem. Apologies for missing that - apparently my brain is on vacation today
aperkins
@ChssPly76 I am attempting to infer what they want based on their code, and I edited it to handle to collection metaphor, which I had missed.
aperkins
@aperkins - your update would work better but it still doesn't handle a case where the collection is empty (and by "doesn't handle" I mean routes control to "do another" which is not what OP implied he wanted). The bottom line here is you can't cheat type erasure. The downvote is not mine, perhaps it'll be removed once they see your update.
ChssPly76
@ChssPly76 I figured it wasn't. I was simply showing them another way to attempt to determine the type that was inside the list without passing the class of T.
aperkins
Oh, also - I am making a minor assumption that if the list is empty, they are likely doing nothing. If this is wrong, then I am wrong with that assumption.
aperkins
Ouch - that's bad advice. If you're doing a runtime check it seems to me that the problem is really with your design. Ask yourself "What am I trying to accomplish with this runtime check and can it be accomplished at compile time?"
arcticpenguin
There are situations where you have to do this kind of behavior and it makes sense. However, I agree that I would tend to avoid this.
aperkins
+5  A: 

It's not clear whether you want to perform the check at compile-time or at runtime. If you simply want to ensure that the list parameter passed to the method contains certain types of objects, then redefine T appropriately.

For example, to ensure that the compiler will only allow a List<Comparable> to be passed to this method, redefine T as:

public <T extends Comparable<? super T>> List<T> doSth(List<T> l) {
    // Method body omitted
}

You can then use method-overloading (instead of an if-else statement), to ensure the correct code is called for any value of T. In other words, replace this:

public <T> List<T> doSth(List<T> l) {

    if(T extends Comparable) {
        // do one thing
    } else {
        // do another
    }

    return null
}

with these:

public <T extends Comparable<? super T>> List<T> doSth(List<T> l) {
    // do one thing
    return null;
}

public <T> List<T> doSth(List<T> l, Class<T> clazz) {
    // do another
    return null;
}

However, you need to remember choosing which overloaded method to call and generic-type checking is compile-time only! For example, the following code:

List<? extends Serializable> alist = new ArrayList<Integer>();
doSth(alist);

will actually call the second doSth method, because the compile-time type parameter (? extends Serializable) does not implement Comparable, even though the runtime type parameter (Integer) does.

Don
A: 

You can do a

public <T extends Comparable<T>> List<T> doSth(List<T> l)

which will allow you to use the Comparable interface on items in 'l'

arcticpenguin
A: 

Well for compile time check Don already gave an answer. For the runtime it's only possible if you also pass a explicit object representing T, for example:

static <T> List<T> doSth(List<T> l, Class<T> tClass)
having tClass object representing real class of T you can check if it have implemented comparable via reflection. But compile-time check is much, much better from my point of view.

Nikolay Ivanov
Whoops missed that Jon already mentioned this option also
Nikolay Ivanov
+1  A: 

You should already know at (even before! :) compile time whether T extends Comparable or not, so why not make two methods?

public <T extends Comparable<T>> List<T> doSthComp(List<T> l) {
  // do one thing
  return l;
}

public <T> List<T> doSth(List<T> l) {
  // do another
  return l;
}
Zed
You can probably be more generous with the bounds there.
Tom Hawtin - tackline
Of course you may *not* know this at compile-time if you're calling it from another generic type... at which point you'd still need the appropriate `Class` object for my solution to work too. I do hate type erasure...
Jon Skeet