How can I cast a list using generics in Java?
Asked Answered
I

6

27

Please consider the following snippet:

public interface MyInterface {

    public int getId();
}

public class MyPojo implements MyInterface {

    private int id;

    public MyPojo(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

}

public ArrayList<MyInterface> getMyInterfaces() {

    ArrayList<MyPojo> myPojos = new ArrayList<MyPojo>(0);
    myPojos.add(new MyPojo(0));
    myPojos.add(new MyPojo(1));

    return (ArrayList<MyInterface>) myPojos;
}

The return statement does a casting that doesn't compile. How can I convert the myPojos list to the more generic list, without having to go through each item of the list?

Thanks

Incardinate answered 19/3, 2009 at 14:53 Comment(0)
I
42

Change your method to use a wildcard:

public ArrayList<? extends MyInterface> getMyInterfaces() {    
    ArrayList<MyPojo> myPojos = new ArrayList<MyPojo>(0);
    myPojos.add(new MyPojo(0));
    myPojos.add(new MyPojo(1));

    return myPojos;
}

This will prevent the caller from trying to add other implementations of the interface to the list. Alternatively, you could just write:

public ArrayList<MyInterface> getMyInterfaces() {
    // Note the change here
    ArrayList<MyInterface> myPojos = new ArrayList<MyInterface>(0);
    myPojos.add(new MyPojo(0));
    myPojos.add(new MyPojo(1));

    return myPojos;
}

As discussed in the comments:

  • Returning wildcarded collections can be awkward for callers
  • It's usually better to use interfaces instead of concrete types for return types. So the suggested signature would probably be one of:

    public List<MyInterface> getMyInterfaces()
    public Collection<MyInterface> getMyInterfaces()
    public Iterable<MyInterface> getMyInterfaces()
    
Inconsequential answered 19/3, 2009 at 14:55 Comment(5)
The 2nd solution is better one IMHO. Returning wild cards is generally considered bad practice, b/c it constrains the client code. In this case with ArrayList<? extends MyInterface> you could only read from the list, and could not add anything to it.Quickly
That may be what's wanted, of course - we just don't know. (It should almost certainly use List<T> instead of ArrayList<T> as well, or possibly just Iterable<T> or Collection<T>.)Inconsequential
+1 for using more generic types (e.g. List<T>, Collection<T>). But returning wildcards is rarely what you want. If you are leaning towards immutability there are better ways of doing it. Naftalin & Wadler talk about this in "Java Generics and Collections".Quickly
Java's handling of generics is terrible compared to C#. It should be perfectly valid to add a Dog to a list of Animals.Cohune
@csmith: It is perfectly valid to add a Dog to a List<Animal>. I agree that C#'s generics is signficantly better, but the second part of your comment is off-base IMO.Inconsequential
P
27

Choosing the right type from the start is best, however to answer your question you can use type erasure.

return (ArrayList<MyInterface>) (ArrayList) myPojos;

Paunch answered 19/3, 2009 at 20:35 Comment(1)
IMO, this should be the answer, since in some cases, you simply can't add the items to a collection of the base type : think of JPA result set from queries, you got a list of JPA entities and if you like to return this list to the caller using some abstracting interface above the entity (persistence agnostic), Peter's advice is the wayBesides
S
5

You should be doing:

public ArrayList<MyInterface> getMyInterfaces() {   
   ArrayList<MyInterface> myPojos = new ArrayList<MyInterface>(0);    
   myPojos.add(new MyPojo(0));    
   myPojos.add(new MyPojo(1));    
   return myPojos;
}
Stuckey answered 19/3, 2009 at 14:56 Comment(0)
L
0

In this case, I would do it like this:

public ArrayList<MyInterface> getMyInterfaces() {

    ArrayList<MyInterface> myPojos = new ArrayList<MyInterface>(0);
    myPojos.add(new MyPojo(0));
    myPojos.add(new MyPojo(1));

    return myPojos;
}

MyPojo ist of type MyInterface (as it implements the interface). This means, you can just create the List with the Interface you need.

Linkoski answered 19/3, 2009 at 14:58 Comment(0)
T
0

Try to use interfaces everywhere except when constructing instances, and you problems will go away:

public List<MyInterface> getMyInterfaces()
{
    List<MyInterface> myInterfaces = new ArrayList<MyInterface>(0);
    myInterfaces.add(new MyPojo(0));
    myInterfaces.add(new MyPojo(1));

    return myInterfaces;
}

As others have said already, the use of MyInterface fixes your problem. It is also better to use the List interface instead of ArrayList for return types and variables.

Tims answered 19/3, 2009 at 16:6 Comment(2)
The use of List isn't what fixes it there though - it's the use of MyInterface instead of MyPojo as the type argument.Inconsequential
Yes, that was misleading, fixed it now.Tims
S
0

If myPojos has been declared as an ArrayList<MyPojo> you cannot just cast it to ArrayList<MyInterface> and return it. Peter Lawrey has shown how you can ignore the ominous screams of impending doom issued by the compiler when you try to do that, (I would add that his solution is missing a @SuppressWarnings( "unchecked" ) statement,) but it is still a very bad idea to do that.

That's because when you return an ArrayList<MyInterface> to the caller, then the caller can add an instance of SomeOtherPojo implements MyInterface into that ArrayList, resulting in your original ArrayList<MyPojo> containing a SomeOtherPojo among the MyPojos, which is a disastrous situation known as Heap Pollution (Wikipedia): attempting to iterate all the MyPojos in the original ArrayList<MyPojo> will throw a ClassCastException.

You might say that in your particular situation you know beyond a shadow of a doubt that none of this will happen, but this is programming by coincidence, and in any case the stackoverflow community has nothing to gain from answers that take into consideration the specifics of your particular situation. So, my advice to you would be to be courteous towards other programmers who might work with your code, follow the principle of least surprise, and do it properly.

You have two options:

  1. Create your ArrayList<MyInterface> and populate it as others have shown.
  2. Do not return an ArrayList<MyMyInterface>; instead, return a List<MyInterface> which has been created either with Collections.unmodifiableList( myPojos ) or (since Java 10) with List.copyOf( myPojos ). When a function returns a list, this list should be immutable. ArrayList is explicitly mutable, so it is unsuitable. Unfortunately, Java does not have an immutable list interface, but most people are accustomed to treating a List<E> returned by a function as immutable and never attempting to invoke any mutation methods on it. If they do, they will get a runtime error, which will be easy to fix, instead of the owner of myPojos receiving a completely inexplicable ClassCastException which is very hard to troubleshoot because it happens at an unknown point in time later.

For an explanation of what List.copyOf() does, and why it is a very good choice, see https://mcmap.net/q/54704/-most-efficient-way-to-cast-list-lt-subclass-gt-to-list-lt-baseclass-gt

Saraisaraiya answered 31/10, 2022 at 8:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.