How to avoid unchecked casts in overriden Collection's methods?
I'm working on a List
implementation. Because of this, I'll have to override the methods
Collection.containsAll(Collection<?> c);
Collection.removeAll(Collection<?> c);
Collection.retainAll(Collection<?> c);
But as it's explained by Sun, they accept collections with any kind of content (note the <?>
). So the collection is not checked by the compiler and it's up to me, to check it. But how to do it? instanceof
on e开发者_开发技巧ach element won't work because of type erasure. The next solution would be to cast each element an catch the ClassCastException
. Look here:
public boolean containsAll( Collection<?> c ) {
boolean foundAll = true;
for ( Object element : c ) {
try {
foundAll &= this.contains( (T) element );
} catch ( ClassCastException e ) {
foundAll = false;
}
}
return foundAll;
}
The other two methods look similar. That works. But it gives me compiler warning "warning: [unchecked] unchecked cast"! Unless I suppress it with "@SuppressWarnings("unchecked")
", it won't compile fine.
I don't want to rely on "@SuppressWarnings("unchecked")
" unless I really have to. Is there a way to avoid it? How would you implement those methods like containsAll(Collection<?> c)
?
edit
Ok, sorry guys, I was not clear enough. I don't extend AbstractList
, and I don't want to. My list is implemented by a balanced binary tree. I have an own implementation of insert()
, remove()
, contains()
(that actually does a search for the leaf), etc, and all take an argument of (generic) type T
. The key goal is to have a sorted list that can be modified while it's iterated through.
So... how do I avoid the warning in containsAll(Collection <?>)
? I have to cast!
Thanks! craesh
You don't need to cast your element to T
when you call contains()
since it is defined as boolean contains(Object o)
. Note that you can ask a Collection<String>
if it contains()
an Integer
object. There's no casting necessary.
remove()
takes an Object
as well, so no casting should be necessary at all.
And by the way: extending AbstractList
takes away most of the boring work of implementing a List
.
Just to clear up a misconception on generics:
instanceof on each element won't work because of type erasure.
No, that is not correct. instanceof
will work just fine. "Type erasure" just means that you cannot get at the compile time type that was declared via generics for the collection you are getting - but you don't care about that anyway.
What you want to check is the run time type of the element you are getting. This is done via instanceof
, is completely independent of generics, and will work.
Of course, as Joachim Sauer points out, you don't even need to check the type in this specific case, so the point is moot anyway...
Edit:
As a matter of fact, Java's AbstractCollection does it just like that:
public boolean containsAll(Collection<?> c) {
Iterator<?> e = c.iterator();
while (e.hasNext())
if(!contains(e.next()))
return false;
return true;
}
(from the Sun JDK sources).
So you should really try to inherit from AbstractList
or at least AbstractCollections
of course you can extends AbstractList
in order to get these methods for free but you can also iterate this
, instead of c
:
@Override
public boolean containsAll(Collection<?> c) {
Iterator<T> it = this.iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
return false;
}
}
return true;
}
or simply:
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : this) {
if (!c.contains(o)) {
return false;
}
}
return true;
}
If you don't want to extend AbstractList, at least extend AbstractCollection. Then you don't have to implement this method at all and the question is moot.
精彩评论