skrebbel
skrebbel

Reputation: 9931

casting Collection<SomeClass> to Collection<SomeSuperClass>

I'm sure this has been answered before, but I really cannot find it.

I have a java class SomeClass and an abstract class SomeSuperClass. SomeClass extends SomeSuperClass.

Another abstract method has a method that returns a Collection<SomeSuperClass>. In an implementation class, I have a Collection<SomeClass> myCollection

I understand that I cannot just return myCollection, because Collection<SomeClass> does not inherit from Collection<SomeSuperClass>. Nevertheless, I know that everything in myCollection is a SomeSuperClass because after all, they're SomeClass objects which extend SomeSuperClass.

How can I make this work?

I.e. I want

public class A
{
  private Collection<SomeClass> myCollection;

  public Collection<SomeSuperClass> getCollection()
  {
    return myCollection; //compile error!
  }
}

The only way I've found is casting via a non-generic type and getting unchecked warnings and whatnot. There must be a more elegant way, though? I feel that also using Collections.checkedSet() and friends are not needed, since it is statically certain that the returned collection only contains SomeClass objects (this would not be the case when downcasting instead of upcasting, but that's not what I'm doing). What am I missing?

Thanks!

Upvotes: 24

Views: 17675

Answers (11)

Mike Nakis
Mike Nakis

Reputation: 61986

Here is a complete solution.

Use of the following static method saves you from warnings about unchecked casts. The seemingly superfluous assignment to result before returning result is actually necessary in order to be able to use @SuppressWarnings.

/**
 * Casts a {@link Collection} to a {@link Collection} of an ancestral element type.
 *
 * @param collection the {@link Collection} to down-cast.
 * @param <T>        the required ancestral element type.
 *
 * @return the same {@link Collection} where the element type is the given ancestral type.
 */
public static <T> Collection<T> downCast( Collection<? extends T> collection )
{
    @SuppressWarnings( "unchecked" ) Collection<T> result = (Collection<T>)collection;
    return result;
}

Use it as follows:

class Ancestor {}
class Descendant extends Ancestor {} 
Collection<Descendant> descendants;
Collection<Ancestor> ancestors = downCast( descendants );

Also works with generics:

class Ancestor<T> {}
class Descendant<T> extends Ancestor<T> {} 
Collection<Descendant<T>> descendants;
Collection<Ancestor<T>> ancestors = downCast( descendants );

Upvotes: 0

Adam111p
Adam111p

Reputation: 3717

Yes we can!

class A {@Override
public String toString()
{

  return "A";
} };
class B extends A {
  @Override
  public String toString()
  {      
    return "B";
  }
};
List<A> l1 = new ArrayList<A>();
l1.add(new A());
List<B> l2 = null;
l2 = (List<B>)(List<? extends A>)l1;
l2.add(new B());

System.out.println( Arrays.toString( l2.toArray() ) );

please test it

Upvotes: 0

Vlad Schnakovszki
Vlad Schnakovszki

Reputation: 8601

Java 8 now offers a neater way of doing it using lambdas: you can use the map() and collect() functions to cast the objects to your super class. A solution for your example would look like this:

public class A
{
    private Collection<SomeClass> myCollection;

    public Collection<SomeSuperClass> getCollection()
    {
        return myCollection.stream().map(x -> (SomeSuperClass) x).collect(Collectors.toList());
    }
}

You can also use other Collectors if needed.

Upvotes: 7

Davi
Davi

Reputation: 11

Based on the top answer given to this question: How do you cast a List of supertypes to a List of subtypes?

I do believe it is possible to do

Collection<SomeSuperType> variable =
                    (Collection<SomeSuperType>)(Collection<?>) collectionOfSomeType;

At the expense of an "unchecked" warning. At the linked question, concern has been expressed about the "hackiness" of this answer due to the lack of type safety. However, in this question's context (namely casting a collection of subtypes to a collection of their supertypes) I see no problems or possible ClassCastExceptions. And in any case, it works quite well.

Upvotes: 1

Konstantin
Konstantin

Reputation: 3696

If you create a new collection of the correct type you can populate it using the old collection

public Collection<SomeSuperClass> getCollection()
{
    return new ArrayList<SomeSuperClass>(myCollection); 
}

Upvotes: 1

ishmeister
ishmeister

Reputation: 609

Will Collection<? extends SomeSuperClass> not do what you want?

Upvotes: 23

Kip
Kip

Reputation: 335

.NET 4.0 introduces covariance and contravariance to generics:

http://msdn.microsoft.com/en-us/library/dd799517(VS.100).aspx

Upvotes: -3

user290466
user290466

Reputation:

perhaps, you should do some workaround.

for example, you should make some "HolderClass" with generic extension, and then you'll be able to put weather your SomeClass, weather your SomeSuperClass.

take a look:

public class HolderClass<E extends SomeSuperClass> {

    private final Collection<E> myClass;

    public HolderClass() {
        myClass = new ArrayList<E>();
    }

    public void putSomeClass(final E clazz) {
        myClass.add(clazz);
    }

    public Collection<E> getMyClasses() {
        return myClass;
    }

} 

and you can make collection this way:

HolderClass<SomeSuperClass> holderClass = new HolderClass<SomeSuperClass>();
holderClass.putSomeClass(new SomeClass());
holderClass.putSomeClass(new SomeSuperClass());

and now, when you make call to getMyClasses(), you'll get collection of SomeSuperClasses

Upvotes: 0

Stefan Hendriks
Stefan Hendriks

Reputation: 4791

I am not sure why; perhaps someone else can explain this; but it works if you do:

public abstract class SomeSuperClass<E> {
    abstract public Collection<SomeSuperClass<E>> getCollection();
}

and:

public class Someclass extends SomeSuperClass {
    @Override
    public Collection<Someclass> getCollection() {
        // TODO Auto-generated method stub
        return null;
    }
}

Upvotes: 0

Everyone
Everyone

Reputation: 2376

A collection of SubType is not substitutable for SuperType.

Upvotes: -3

Steven Schlansker
Steven Schlansker

Reputation: 38526

You can't do this. You have a Collection<SomeClass> and wish to return a Collection<SuperClass>

Now imagine that someone has your returned Collection - and they try to insert a different subclass of SuperClass, SomeOtherClass. This should be allowed on your SuperClass collection - but it can't because it's actually a Collection<SomeClass>, unbeknownst to anyone but the private member of A.

Upvotes: 11

Related Questions