Matt Huggins
Matt Huggins

Reputation: 83289

Java: Casting from List<B> to List<A> when B implements A?

I have the following class & interface defined:

public interface A {
}

public class B implements A {
}

I have a List of B objects that I need to cast to a List of A objects:

List<B> listB = new List<B>();
listB.add(new B());  // dummy data
listB.add(new B());  // dummy data
listB.add(new B()); // dummy data

List<A> listA = (List<A>) listB;

The last line above results in compile error "Cannot cast from List<B> to List<A>". I attempted to work around this with this following instead:

List<A> listA = Arrays.asList((A[]) listB.toArray());

Unfortunately, that throws a ClassCastException. Does anyone know how I can resolve this?

Upvotes: 6

Views: 6244

Answers (7)

Dhruvan Ganesh
Dhruvan Ganesh

Reputation: 1580

We can achieve this using Generics and Wildcards. You might not be able to create List<A> but create something like List<? extends A> which will call all the methods of the interface which is enough for most I think.

Creating a new collection is expensive and not advisable. Since the references of the same objects will be passed on into the new collection, the additional list creation step is just an overhead.

List<B> bList = new ArrayList<B>();

List<? extends A> aList = bList;

Upvotes: 0

It is just a matter of declarations. Don't declare listB as List but as List or List.

List<A> listB = new List<B>();
listB.add(new B());  // dummy data
listB.add(new B());  // dummy data
listB.add(new B()); // dummy data

List<A> listA = listB;

As simple as that. If you want to force listB to be List (avoiding the addition of any non-B element) you will be forced to use:

List<A> listA = new ArrayList<A>(listB);

But as they already pointed out, if you are forced to do that it is not a good sign.

Upvotes: 0

newacct
newacct

Reputation: 122489

Rather than fighting against a language feature, you should learn to use it properly.

Rather than asking how to use unsafe casts to work around the error, it is instructive to first understand why there's an error; why what you're asking to do is unsafe (because you can add an A to the list using listA and then take it out of the list as a B using listB).

Then, you should explain why you think that your use case does not run into the unsafe scenario. Because the answer to this will give a hint for how to change your code to make it work correctly with generics.

  • If you say that it is not unsafe because you never add anything using listA, then you should change the type of listA to List<? extends A>. That way, you can assign listB to it directly without any casts, and you won't be able to add anything to it using listA (except null).
  • If you say that it is not unsafe because you never get anything out using listB, then why not change the type of listB to List<A> in the first place?

Upvotes: 10

Peter Lawrey
Peter Lawrey

Reputation: 533700

You can just use type erasure if you know the operation is safe. This produces a warning which you can turn off using @SuppressWarnings

List<A> listA = (List) listB;

The reason the compiler has difficulty with a plain cast is that you can now add a class C which also implements A. Except your original list has been altered and now contains a C even though you have specified that it shouldn't.

Upvotes: 6

irreputable
irreputable

Reputation: 45443

List<A> listA = (List<A>)(List<?>) listB;

not recommended.

Upvotes: 4

Adam
Adam

Reputation: 325

I'm sure you need make the whole list List< A >(weird parsing there) and when adding new B objects cast them with (A)

listA.add( (A) new B());  // dummy data

Upvotes: 1

BalusC
BalusC

Reputation: 1109262

You cannot cast it like that. Create a new one:

List<A> listA = new ArrayList<A>(listB);

The constructor takes Collection<? extends A>. It will point to the same references anyway.

Upvotes: 17

Related Questions