Reputation: 453
I'm writing a method which filters a specific Collection with a "Predicate" and returns a new Collection containing only filtered elements (those for whom Predicate returns true).
Something like this :
public <T> Collection<T> filter(Collection<T> collection, Closure<T> predicate);
I know that, in Java, I can't just create a new Collection()
at runtime, because of type-erasure.
I also know the "work-around" by passing an extra-argument to the method to call T.newInstance().
This would look like :
public <T> Collection<T> filter(Class<? extends Collection<T>> collectionToInstanciate, Collection<T> collection, Closure<T> predicate) {
// create the new Collection
Collection<T> container = collectionToInstanciate.newInstance();
// and then add only filtered items
Iterator<T> iter = collection.iterator();
while (iter.hasNext()) {
T obj = iter.next();
// if Predicate.invoke() returns true, then keep element, otherwise skip it
if (predicate.invoke(obj)) {
container.add(obj);
}
}
return container;
}
But how should I call my method ?
For instance, if I want only odd numbers of a List of Integers, I'd like to do :
// instanciate ArrayList<Integer> = [1, 2, 3, 4, 5]
ArrayList<Integer> array = ...;
// return a new LinkedList<Integer> with only odd numbers
filter(LinkedList<Integer>.class, array, new Closure<Integer>() {
public Boolean invoke(Integer arg_p) {
return (arg_p % 2 == 0);
}
});
// should return [2, 4] as a LinkedList<Integer>
The problem is that
LinkedList<Integer>.class
doesn't compile.
How should I declare that to correctly instanciate a LinkedList in the filter() method ?
Regards,
Upvotes: 1
Views: 796
Reputation: 453
Another way around, from Peter Lawrey's answer and LuiggiMendoza's comment, is to use the filter() method this way :
List<Integer> result = filter(
LinkedList.class, // instead of new LinkedList<Integer>()
array,
new Predicate<Integer>() {
public boolean invoke(Integer arg_p) {
return (arg_p % 2 == 0);
}
});
And in the filter() method :
public <T> Collection<T> filter(Class<? extends Collection> collectionToInstanciate, Collection<T> collection, Closure<T> predicate) {
// create the new Collection as a raw Collection, and cast it back to Collection<T>
Collection<T> container = (Collection<T>) collectionToInstanciate.newInstance();
// and then add only filtered items
Iterator<T> iter = collection.iterator();
while (iter.hasNext()) {
T obj = iter.next();
// if Predicate.invoke() returns true, then keep element, otherwise skip it
if (predicate.invoke(obj)) {
container.add(obj);
}
}
return container;
}
Upvotes: 0
Reputation: 533492
Generics are a compile time feature and have little meaning at runtime. If you want to create a LinkedList, that is what you do. You can't make the compiler give you an error based on something you do at runtime.
A simpler solution is to pass an instance of the class you want to populate.
List<Integer> result = filter(new LinkedList<Integer>(), array,
new Predicate<Integer>() {
public boolean invoke(Integer arg_p) {
return (arg_p % 2 == 0);
}
});
It is marginally shorter and can be checked at compile time.
Note: many of these predicates are much simpler and faster as a plain loop.
List<Integer> result = new LinkedList<Integer>();
for(Integer i: array)
if (i % 2 == 0)
result.add(i);
Upvotes: 3