I am currently working on a homework assignment for a Java programming course I am taking. I am not asking for an exact answer, but for some guidance.
The problem I'm working on is this:
I have a filter class that implements a Filter interface. This interface has just one method - matches(T element)
I have configured my filter method to check an Integer that is being passed in for prime-ness.
There is also a decorator class that decorates a collection class to only display objects that pass the filter.
I'm having problems getting the contains(Object o) method to work correctly.
Basically the contains(Obj o)
method in the FilteredCollection
class should first check to see if the object passes the filter, and then if it does, call the undecorated contains()
method on that object.
Assuming I want to be able to use this FilteredCollection
class with many different types of filters, How can I determine what type of object is being passed in, and then be able to pass that object to the current Filter that's being implemented.
Here is my PrimeNumberFilter
Class:
public class PrimeNumberFilter implements Filter<Integer> {
public boolean matches(Integer e) {
int n = e.intValue();
if (n != 2 && n % 2 == 0) {
return false;
}
for (int i = 3; i * i <= n; i += 2) {
if (n % i == 0) {
return false;
}
}
return true;
}
}
Then here is my shortened FilteredCollection
Class:
class FilteredCollection<T> implements Collection<T> {
Collection<T> fc;
Filter<T> currentFilter;
private FilteredCollection(Collection<T> coll, Filter<T> filter) {
this.fc = coll;
this.currentFilter = filter;
}
public static <T> FilteredCollection<T> decorate(Collection<T> coll,
Filter<T> filter) {
return new FilteredCollection<T>(coll, filter);
}
public boolean contains(Object o) {
//What do I do here?
return fc.contains(o);
}
The object being passed in to the contains method has to pass the filter, in this case a PrimeNumberFilter
.
The error I'm getting is it keeps wanting to cast the object to type T, and I know that this will never work because of erasure.
I've done a ton of research, and I've boiled it down to needing to use reflection.
The only hint my instructor will give me is that object only has a few methods I can use, and I should use one of those.
Thanks for your help!
EDIT: One of the requirements of the project is to NOT cast an object to T in any method. So while these answers are great, I am not able to use any of them.
The method to use is Object.equals(Object)
. You can iterate the collection fc
and check if it contains an element wich equals(o)
. If so, continue on with said element (which is of type T
).
for(T e : fc) {
if(o.equals(e)) {
// carry on with e
}
}
You might also want to cover o == null
.
There is nothing wrong with your code.
The problem is due to the java.util.Collection.contains(Object o)
interface method not being generically typed. This is outside of your control.
Option 1: Simple approach
In your implementation of that method you can cast:
public boolean contains(Object o) {
return o != null && currentFilter.matches((T)o) && fc.contains(o);
}
Option 2: Add a getParameterType() method to the Filter interface
This method would return the generic type of the filter as implemented in the various subclasses.
interface Filter<T> {
boolean matches(T parameter);
Class<T> getParameterType();
}
Then...
public boolean contains(Object o) {
return o != null && currentFilter.getParameterType().isAssignableFrom(o.getClass()) && currentFilter.matches((T)o) && fc.contains(o);
}
Option 3: Determine generic type via reflection
Technically the generic type of your filter will not actually be erased at runtime. Type erasure does not apply here because PrimeNumberFilter is an actual class which implements a generically typed interface.
@SuppressWarnings("unchecked")
public boolean contains(Object o) {
Class<?> genericTypeOfFilter = getGenericTypeOfFilter(currentFilter);
return o != null && genericTypeOfFilter.isAssignableFrom(o.getClass()) && currentFilter.matches((T)o) && fc.contains(o);
}
static <T> Class<T> getGenericTypeOfFilter(Filter<T> filter) {
try {
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
Class<T> type = (Class<T>) ((ParameterizedType)filter.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
return type;
}
catch (Exception e) {
throw new IllegalStateException("Unexpectedly failed to read generic type of filter: " + filter, e);
}
}
If it was my code I'd go with Option 2 in this case, it is more robust than relying on reflection.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With