Reputation: 189906
I get a warning about unchecked casts on the "return (T) value;" line. Is there a better way to do this, or should I just suppress the warning?
class SomeClass<T>
{
/* ... other methods ... */
private Set<T> aSet;
public T filter(Object value)
{
if (this.aSet.contains(value))
return (T) value;
else
return null;
}
}
edit: I'm stuck with public T filter(Object value)
as a signature.
Upvotes: 4
Views: 228
Reputation: 19189
Elaborating on Tom Hawtin's answer, you have the option of using a Map<T,T>
instead, which gets around the casting issue. If you're using a HashSet<T>
for aSet
's implementation, HashSet
uses a HashMap<T,HashSet<T>>
behind the scenes anyway (it uses references to itself as the values - not sure if there's a reason for this other than choice - with the set's "values" as keys) and performs the bulk of its operations just by reinterpreting the return values of the Map
functions.
Consequently, you could do this, if you wanted to (and I don't see an immediate reason why it would be any less performant than a HashSet
):
class SomeClass<T>
{
/* ... other methods ... */
private Map<T,T> aSet;
public T filter(Object value)
{
// Will return the properly-typed object if it's in
// the "set" otherwise will return null
return aSet.get(value);
}
}
Upvotes: 2
Reputation: 147164
Perhaps you could replace:
private Set<T> aSet;
with
private final Map<T,T> aSet;
The Set
is likely implemented as a Map
anyway.
Upvotes: 2
Reputation: 7826
The problem with doing it that way is that it's a ClassCastException waiting to happen. If you have an object that is not of class T
, but is equals
with one of your elements, when you try to cast it to T it will fail. What you need is to get the actual value that is contained in the set, which you know is of type T. Unfortunately Set doesn't have a get method, so you need to temporarily convert it to a collection that does have that like a List.
You could try doing it like this:
private Set<T> aSet;
public T filter(Object value) {
List<T> list = new ArrayList<T>(aSet);
int index = list.indexOf(value);
if (index == -1) {
return null;
}
T setValue = list.get(index);
return setValue;
}
This might have a slower performance, but it offers a bit more type safety.
Upvotes: 0
Reputation: 421340
It's possible :)
public T filter(Object value)
{
if (this.aSet.contains(value)) {
Set<T> tmp = new TreeSet<T>(aSet);
tmp.retainAll(Collections.singleton(value));
return tmp.iterator().next();
}
return null;
}
but it's obviously uglier than doing a cast.
Upvotes: 2
Reputation: 114847
In this case you could suppress the warning and leave a comment why it is save:
if (this.aSet.contains(value)) {
// this following cast from Object to T is save, because ...
@SuppressWarnings("unchecked") T result = (T) value;
return result;
} else
return null;
There's an alternative but that would require, that the class or the method 'knows' it's parametized type. Then we can cast without a warning (but should comment too). I show a solution where we introduce / change a constructor to store the 'generics' information as a field:
public class SomeClass<T> {
private Set<T> aSet;
private Class<T> genericType;
public SomeClass(Class<T> genericType) {
this.genericType = genericType;
}
public T filter(Object value)
{
if (this.aSet.contains(value))
// this following cast from Object to T is save, because ...
return genericType.cast(value);
else
return null;
}
}
Upvotes: 3
Reputation: 35761
What about using the generic type argument as the argument type to filter.
class SomeClass
{
/* ... other methods ... */
private Set<T> aSet;
public T filter(T value)
{
if (this.aSet.contains(value))
return (T) value;
else
return null;
}
}
Ok, since you're stuck with the object
signature, you don't have any other chance but do disable/ignore the warning. Java Generics are no "real generics" in the sense that the underlying type system supports them. In fact, they're just a compiler thing since they're based on Type Erasure. Performance penalities and possibly unsafe casting is the price for maintaing binary compatibility with older versions of the JVM.
You can contrast that with the .NET CLR that has real generics, I've written a blog post comparing the two approaches recently to which you can also refer if any of what I said above left you confused.
Upvotes: 4
Reputation: 17977
Is this what you mean?
public class SomeClass<T>
{
private Set<T> aSet;
public T filter(Object value)
{
return (aSet.contains(value) ? (T) value : null);
}
}
Upvotes: 1