4

The stackoverflow question: Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic? has many correct answers that point out that you can add a Cat to List<Animal> but not to List<Dog>. This leads to the use of constructs like List<? extends Animal>. I have found that there are cases where this is just not convenient and therefore I have defined classes to "down cast" the collection. (See below for an example of DownCastCollection). My question is whether you can present some cases where the best approach is to downcast? If you think it is never the best approach then can you explain why? I realize this is a bit open ended but I think the answers may be very helpful since this situation is common. I do agree that in most cases we should use the `Collection, but I am pointing out that sometimes that is just not the best approach. Have you encountered examples where this is not the best approach and if so can you present them here?

Based on the answer in https://stackoverflow.com/a/27491199/4350148 i have changed DownCastCollection to be modifiable and so now it does not return an error. The question still is valid.

Here is the downcast collection class:

import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class DownCastCollection<E> extends AbstractCollection<E> implements Collection<E> {
@SuppressWarnings("rawtypes")
private Collection delegate;

public DownCastCollection(Collection<? extends E> delegate) {
    if(delegate == null) throw new IllegalArgumentException();
    this.delegate = delegate;
}

@Override
public int size() {
    return delegate.size();
}

@Override
public boolean isEmpty() {
    return delegate.isEmpty();
}

@Override
public boolean contains(Object o) {
    return delegate.contains(o);
}
private class MyIterator implements Iterator<E>{
    @SuppressWarnings("rawtypes")
    Iterator delegateIterator;

    protected MyIterator() {
        super();
        this.delegateIterator = delegate.iterator();
    }

    @Override
    public boolean hasNext() {
        return delegateIterator.hasNext();
    }

    @SuppressWarnings("unchecked")
    @Override
    public  E next() {
            return (E)delegateIterator.next();
    }

    @Override
    public void remove() {
        delegateIterator.remove();

    }

}
@Override
public Iterator<E> iterator() {
    return new MyIterator();
}



@SuppressWarnings("unchecked")
@Override
public boolean add(E e) {
    return delegate.add(e);
}

@Override
public boolean remove(Object o) {
    return delegate.remove(o);
}

@SuppressWarnings("unchecked")
@Override
public boolean containsAll(Collection<?> c) {
        return delegate.containsAll(c);
}

@SuppressWarnings("unchecked")
@Override
public boolean addAll(Collection<? extends E> c) {
    return delegate.addAll(c);
}

@SuppressWarnings("unchecked")
@Override
public boolean removeAll(Collection<?> c) {
        return delegate.removeAll(c);
}

@SuppressWarnings("unchecked")
@Override
public boolean retainAll(Collection<?> c) {
        return delegate.retainAll(c);
}

@Override
public void clear() {
    if(delegate == null) return;
        delegate.clear();

}
Community
  • 1
  • 1
dan b
  • 1,172
  • 8
  • 20

4 Answers4

1

Collections.unmodifiableCollection and its friends are built-in ways to view a Collection<? extends T> as a Collection<T>. They resolve exactly the issue you have correctly identified, that elements can be added to a Collection<T> that cannot be added to a Collection<? extends T>, by forbidding additions or any sort of modification.

Other solutions to this problem include not depending on the implementation details of the underlying objects, forbidding addition but not removal, or making copies.

Finally, for what it's worth, your solution is exactly as type-safe, but less efficient, than simply casting your collection (Collection<E>) (Collection) collection, which takes advantage of erasure to simply (and unsafely) cast the backing collection.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • fantastic answer! Thank you. One small difference it that the downcast does allows removal. In any case, this is a great answer. – dan b Dec 15 '14 at 23:29
  • After some thought on this, I still appreciate your answer, but I realized that this is not about unmodifiable. The question is whether it might make sense to downcast a collection. You answer is still very helpful,but I would like to see the question of whether downcast is sometimes the best way to go, even in the case where the result is modifiable. – dan b Dec 16 '14 at 09:46
  • However as a result of your answer, I changed the downcast class to be modifiable. I would love to hear your further thoughts on this. Also take a look at http://stackoverflow.com/a/27487926/4350148 where I describe an example scenario where downcast might be useful – dan b Dec 17 '14 at 03:14
  • I'd tend to deny that `add` should be supported under any circumstances; it'd make more sense for the original collection's type to be changed – Louis Wasserman Dec 17 '14 at 03:15
  • Have you seen my answer in http://stackoverflow.com/a/27487926/4350148. There is a case where it might be useful. – dan b Dec 17 '14 at 03:17
  • I don't buy it. "You might argue that the method can return Collection extends Result> however, that would alter the interface IFace." You _should_ either alter the interface, and forbid the the caller of the method from adding non-`ResultImpl`s to that list, or you should accept the cost of the copy. – Louis Wasserman Dec 17 '14 at 03:19
  • (Returning mutable data from APIs is generally a code smell anyway?) – Louis Wasserman Dec 17 '14 at 03:22
  • Altering the interface is not an option as that would alter the contract. So therefore copy, but that can be expensive as we are talking about potentially millions of results. What is the concern about downcast in that case? – dan b Dec 17 '14 at 03:23
  • If you can't return an unmodifiable collection, and you can't change the API, then you've blocked yourself in, but it doesn't make the design good -- just unchangeable. – Louis Wasserman Dec 17 '14 at 03:24
  • The interface is determined by the "requirements" and in this case the caller needs to know that the collection is always of type Result. For example, the entrySet of Map return Set> not Set extends Map.Entry> – dan b Dec 17 '14 at 03:26
  • True -- and that `Set` doesn't support addition, so I'm okay with that. From the spec: "The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll and clear operations. It does not support the add or addAll operations." – Louis Wasserman Dec 17 '14 at 03:36
  • So is the criteria for whether a method should return Collection extends T> or Collection to be based on whether the result supports addition. In all cases you are suggested that is the criteria? Hope you are having fun with this! – dan b Dec 17 '14 at 03:40
  • The case you've described where you _need_ `ResultImpl` elements to manipulate initially, instead of just the plain `Result` references, is frankly pretty unusual. I wouldn't say there's a general rule other than "prevent a situation where you need to add a `T` to a `List extends T>`, which can take a couple forms: not depending on the details of your `T` subclass, preventing modification, and the like. – Louis Wasserman Dec 17 '14 at 03:49
  • Agreed. It is rare and should be avoided. Nevertheless it is exactly what I encountered in coding a complex application. Thanks for your input. Can you modify your answer? I like to have an accepted answer with the subtleties in these comment. – dan b Dec 17 '14 at 12:07
  • Wow. That is a great observation about the casting. You are quite good at this! – dan b Dec 17 '14 at 18:26
0

With a collection of a generic type ? extends E, it is not legal to add new elements as the compiler cannot verify the exact type of the collection that your variable points to. Instead, the compiler can only tell that the collection represents any collection of subtypes of E. Thus:

Collection<? extends Something> collection = ...
collection.add(new Something());

does not compile. With your version of a DownCastCollection, the example would compile but instead throw a runtime exception. You migrate a compile-time error to become a runtime error. This is not a good idea as it introduces potential errors to your application that your compiler could otherwise catch.

The only reason when casting a generic collection makes sense would be when you for some reason know the actual type of the collection but you were not able to express this by your program's types. Better that a cast is normally to create a new collection:

Collection<? extends Something> collection = ...
Collection<Something> other = new HashSet<>(collection);
Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
  • The DownCastCollection is creating an "almost immutable" collection. I say almost because it allows removing but not adding. I could make it immutable by adding the throw UnsupportedOperationException to the remove methods. That is the standard approach to making an immutable collection. I am intending to say that when collections are immutable its OK to downcast. Thanks for taking the time to think about this. – dan b Dec 15 '14 at 12:46
  • You are welcome. You are however allowed to remove elements from a wildcard collection, in this sense, this collection is also "almost immutable". – Rafael Winterhalter Dec 15 '14 at 13:05
  • I am also pointing out that sometimes it's best to use this DownCastCollection and if you do, and you use it incorrectly at least you will get a runtime exception, allowing you to debug the problem, and indicating a programmer error. – dan b Dec 15 '14 at 13:16
  • What I am looking for is counter examples or a good argument as to why one should never do this. I present counter example where I believe downcast makes sense http://stackoverflow.com/a/27487926/4350148. I would love to hear more from you about this but my question remains unanswered. – dan b Dec 15 '14 at 22:31
0

I usually use List<? extends T> if I have a superclass or interface that should allow its subclasses or implementations to specify the return value of a method more precisely.

Given you have the following interface.

public interface Foo {
    public List<CharSequence> doSomething();
}

If you now want to create a class Bar implements Foo, the method signature of doSomething() will have to be List<CharSequence> doSomething() and nothing else.

However, if you define doSomething() like this...

public interface Foo {
    public List<? extends CharSequence> doSomething();
}

Bar's doSomething() might have a method signature that looks like this.

public class Bar implements Foo {
    public List<String> doSomething() {
        // ...
    }
}

Besides that, you should also pay attention to what @raphw said, you can't add elements to a List<?> or List<? extends T>.

mezzodrinker
  • 998
  • 10
  • 28
  • I appreciate your comment. I follow your approach in my code and I think it is helpful to readers. However I am looking for counter examples where down casting makes sense. To argue that it does not make sense at all is more difficult but doable. Giving one counter example does not prove that it is never useful. To see what I mean take a look at http://stackoverflow.com/a/27487926/4350148 where I show where it might be useful. – dan b Dec 15 '14 at 19:41
0

Here is a scenario where the DownCastCollection class is useful. Suppose I have an interface (lets call it IFace) that has a method that returns Collection<Result>. Suppose that Impl is an implementation of IFace. In order to compute the Collection of Results for the method, Impl uses a particular implementation of the Result interface, called ResultImpl and maintains a list of Collection<ResultImpl>. ResultImpl has methods that are not part of the interface and hence maintaining Collection<Result> in that method does not work without casting up. My implementation of the method needs to return Collection<Result> from the Collection<ResultImpl> and this is where the DownCastCollection is useful. You might argue that the method can return Collection<? extends Result> however, that would alter the interface IFace. In other words the method wants the guarantee that Collection<Result> is returned. We may also want to allow the caller of the method to modify the final result by adding to it or removing from it. In that case we still need the returned result to be modifiable.

dan b
  • 1,172
  • 8
  • 20