Edi
Edi

Reputation: 73

better solution with cast in generic java

Suppose I have the following code:

Object li = new Object(); 

// this causes a compiler warning   

ArrayList<String> lo = (ArrayList<String>)(li); 

Okay, I have edited the question, sorry, I made a mistake, the point is, since it will cause a compile time warning when we cast to the target, whose target is a parameterized type, is there any better solution so that I can prevent the code from causing a compile time warning?

When dealing with arrays, we have Arrays.newInstance to replace the use of cast.


code from original question:

// List<String> is a superclass of ArrayList

List<String>      listOne =  new List<String>();

ArrayList<String> listTwo = (List<String>) listOne; 

Upvotes: 0

Views: 122

Answers (2)

Paul Bellora
Paul Bellora

Reputation: 55213

If you are in such a situation, you have two options. The first is to do an unchecked cast:

Object o = getObjectFromSomewhereMysterious();
List<String> lst = (List<String>)o; //compiler warning

This will cause the warning you mention - what it means is that, because of type erasure, at runtime the cast can only check to make sure o is a List, but not a List<String>. So for example if o is a HashMap the cast will fail immediately, but if it's a List<Integer> it won't fail... until some later time when you try to treat an element of lst like a String. This is known as "polluting the heap":

//some later place in the code path - who knows, it could be miles away
String str = lst.get(0); //ClassCastException if lst isn't really a List<String>

As long as you can be sure that the cast is safe, you can suppress the compiler warning:

@SuppressWarnings("unchecked") //this is safe because blah blah blah
List<String> lst = (List<String>)o;

Always document why the warning can be suppressed - this helps to keep the code maintainable.

The second option is to play it safe:

List<?> lst = (List<?>)o;

This means lst is a List of some unknown type. It allows you to avoid the unchecked cast, but places restrictions on what you can do with lst:

lst.add("some string"); //compiler error

That statement is illegal since we don't know whether lst is allowed to hold Strings. The best we can do is read from it, but even then elements are only typed as Object:

Object element = lst.get(0);

Upvotes: 1

jahroy
jahroy

Reputation: 22692

You can't instantiate a List, because List is an interface.

An ArrayList is a class that implements the List interface.

You can do this:

List<String> listOne  =  new ArrayList<String>();
List<String> listTwo  =  listOne;

This is the correct approach when the code using listOne doesn't care what kind of List it is.

Here's an example that demonstrates why this is a good approach:

List<String> someList = createList();

Here someList is set to the return value of a method named createList. In this situation we have no idea what kind of list someList is. It could be an ArrayList, a Vector, a Stack, etc... As long as createList returns an object that implements the List interface, the above code will work. With this approach, the code in createList can be modified without affecting the code that calls it.

You can also do this:

ArrayList<String> listOne  =  new ArrayList<String>();
ArrayList<String> listTwo  =  listOne;

This is not as flexible, but it allows you to treat your lists specifically as ArrayLists.

Technically, you can do this, but it's not a good idea:

List<String>      listOne  =  new ArrayList<String>();
ArrayList<String> listTwo  =  (ArrayList<String>) listOne;

It's better to program to an interface by declaring a List and instantiating an ArrayList.

Here's an example that shows some benefits of programming to interfaces:

public static void testIt() {

    List someList;
    ArrayList anArrayList;

    /*
     *  all of these will work 
     */

    someList = createVectorAsList();
    printList(someList);

    someList = createStackAsList();
    printList(someList);

    someList = createArrayListAsList();
    printList(someList);

    // you CAN assign an ArrayList to a List

    someList = createArrayList();
    printList(someList);

    // you CAN treat an ArrayList as a List

    anArrayList = createArrayList();
    printList(anArrayList);


    /* 
     *  none of these work 
     */

    // you can NOT assign List to an ArrayList

    anArrayList = createStackAsList();
    anArrayList = createVectorAsList();

    // you can NOT pass anything but an ArrayList to printArrayList

    printArrayList(createStackAsList());
    printArrayList(createVectorAsList());
    printArrayList(createArrayListAsList());

}

/** Prints any List */

public void printList(List someList) {
    for (Object o : someList) {
        System.out.println(o.toString());
    }
}

/** Prints ArrayLists only */

public void printArrayList(ArrayList someList) {
    for (Object o : someList) {
        System.out.println(o.toString());
    }
}

public List createVectorAsList() {
    Vector v = new Vector();
    v.add("I am a vector element");
    return v;
}

public List createStackAsList() {
    Stack s = new Stack();
    s.add("I am a stack element");
    return s;
}

public List createArrayListAsList() {
    ArrayList ar = new ArrayList();
    ar.add("I am an array list element");
    return ar;
}

public ArrayList createArrayList() {
    ArrayList ar = new ArrayList();
    ar.add("My array is not returned as a list...");
    return ar;
}

Upvotes: 1

Related Questions