views:

140

answers:

4

I'm wondering if is possible pass types by argument in Java.

Let me explain better:

Suppose the next code

class Bee {
    // Class implementation
}

class Worker extends Bee {
    // Class implementation
}


class Queen extends Bee {
    // Class implementation
}

And now create a container with my bees objects

Vector<Bee> x=new Vector<Bee>();
// ...
for(i=0;i<10;i++)
    x.add(new Worker());
// ...
for(i=0;i<10;i++)
    x.add(new Queen());

Now I want create a generic method that iterates the vector and returns a set with a specific type of bees, Worker or Queen. How to do this?

EDIT

I tried

search(x,Worker.class);

static public <T extends Bee> Set<T> search(List<Bee> bees, Class<T> clazz){
    // ...
}

and I got an error reporting "The method search(List, Class) is no applicable for the arguments (Set, Class)". The problem is on 2nd argument because the types are incompatible.

+5  A: 

You can use instanceof expression to test if a Bee is a Worker or a Queen. You can use this to filter your Vector<Bee> (or even better, a List<Bee>) according to the subtypes.

Guava also provides Iterables.filter(Iterable<?> unfiltered, Class<T> type) that you can use.

polygenelubricants
Why was this downvoted? I'm too tired to write an essay right now but surely my answer isn't useless?
polygenelubricants
You get an upvote from me for compensation. Your answer **is** useful.
Andreas_D
@Andreas: and upvote back from me as well since yours actually have an example. Maybe I shouldn't stackoverflow when I'm tired.
polygenelubricants
@Andreas: I think I get why our answers are downvoted: they're not really generic methods per se. With `instanceof` you have to hardcode the types. I still stand by my Guava recommendation, though. So much so that I'll try to provide an example snippet. Maybe tomorrow.
polygenelubricants
+1 for the Guava suggestion; I don't think instanceof fulfills the requirements of being generic as you say.
Mark Peters
BTW it seems to me they kind of blew it with the spec for that method. It should be `Class<? extends T> clazz` so that you can choose what the type parameter of the resultant collection is (it would still default to T so no harm). You couldn't use it to produce an `Iterable<Bee>` of only worker bees, for example.
Mark Peters
+3  A: 

Use the instanceof operator:

public static Set<Queen> queensOnly(Collection<Bee> bees) {
  Set<Queen> queens = new HashSet<Queen>();
  for (Bee bee:bees) {
    if (bee instanceof Queen) {
      queens.add((Queen) bee);
    }
  }
  return queens;
}
Andreas_D
A downvote? For a solution that fulfills the requirement? C'mon, downvoter, tell me whats wrong!
Andreas_D
Some people kneejerk when they see `instanceof`. =)
Alan Krueger
@Andreas_D OP wanted to pass types as an argument (see question title and also the first line of the question), so this does not fulfill the requirement. Sorry, the other answer is the correct one, your is not.
unbeli
@unbeli - Ah, so when this is a stict requirement, then it can't be fulfilled because, no, we can't pass a type as an argument: a `Class` object is not a "type". A type is nothing but a concept. And concepts can't be passed to methods, only primitives and object references. But anyway, 'the other' answer is closer to the idea, the OP had in mind.
Andreas_D
@Andreas_D when you say 'pass an integer', everyone understands, that you pass an object, representing an integer, not the concept of integer number. When you say 'pass a type', it mean pass an object, representing a type, which would be a Class in Java. Don't take downvotes that personal ;)
unbeli
+4  A: 

Use the Class as an argument to the method

public <T extends Bee> Set<T> filterBeesByType(List<Bee> bees, Class<T> clazz)

by the way, it is a good practice to refer to collection types by their interface (Set, List, etc) rather than implementation (Vector).

matt b
it would be more appropriate to filter a List into a List, not into a Set. Also, public should be before the generic declaration. Otherwise, this is a correct answer.
unbeli
How is the call filterBeesByType?filterBeesByType(listOfBees, ????? )
Ricardo
@So Many Questions: filterBeesByType(listOfBees, Worker.class)
unbeli
@unbeli, thanks, missed the `public` error. As for the question of the correctness of `Set` vs `List` I think it completely depends on the intention, if the goal of this piece of code is to simply filter the `List` down to types that match `clazz` or to also remove duplicates. The original question mentioned returning a Set. Both are valid cases.
matt b
The filtering logic is `if (clazz.isInstance(bee)) ...`, assuming you want subtypes to match too.
Mark Peters
+4  A: 

From your edit I see, you're still struggling with the implementation. Here is a working example:

public static void main(String[] args) {
    Collection<Bee> bees = new ArrayList<Bee>();
    bees.add(new Hive.Worker());
    bees.add(new Hive.Queen());
    Set<Queen> queens = getBeesOfOneType(bees, Queen.class);
}

private static <T extends Bee> Set<T> getBeesOfOneType(Collection<Bee> bees, Class<T> beeType) {
    Set<T> result = new HashSet<T>();
    for (Bee bee : bees) {
        if (beeType.isInstance(bee)) {
            // The following cast is safe. The previous if makes sure that
            // that bee can be cast to T at this place
            T beeTypeInstance = beeType.cast(bee);
            result.add(beeTypeInstance);
        }
    }
    return result;
}

There is still one Type safety warning and I'm pretty sure that this one can't be eliminated. The compiler doesn't now, that we select only the T type bees, so it has to warn us.

Andreas_D
Good job. It is exactly what I want. Thank you.
Ricardo
Thanks a lot :-)
Andreas_D
You should suppress that warning as you can prove it is safe. Moving that cast into the right side of a declaration of a local variable will let you minimize the scope of the @SuppressWarnings.
ILMTitan
@ILMTitan - thanks for your comment, never used the annotation that way before and it will not be the last time! Learned something new today :)
Andreas_D
Why not use `beeType.cast(bee)` and avoid the unchecked cast altogether? Can't upvote until I see that.
Mark Peters
@Andreas: It doesn't matter how it does it, what matters is what it says it does. You'll never get a ClassCastException since you already performed `beeType.isInstance`...I'm not saying don't use isInstance first! You may prefer annotating and commenting your own source code, but you're wrong unfortunately. Use library functions, especially when they were created with the **EXACT** purpose of avoiding what you're doing here.
Mark Peters