简体   繁体   中英

Why does casting an ArrayList 's generic to a superclass not work?

Can someone please explain to me why the line marked //this line gives a compile error (why?) in the following code sample does not work?

import java.util.ArrayList;

public class GenericCastCheck {

    class A{
    }

    class B extends A{
    }

    public static void main(String[] args) {

        A aObject = new A();
        B bObject = new B();

        //this line works fine
        aObject = bObject;
        //this line gives a compile (expected)
        bObject = aObject;

        ArrayList<A> aList = new ArrayList<A>();
        ArrayList<B> bList = new ArrayList<B>();

        //this line gives a compile error (why?)
        aList = bList;
        //this line gives a compile error (expected)
        bList = aList;
    }
}

Specifically, when we say that bList is of type ArrayList<B> , does it not mean that each element of it is an instance of B ? If so, then what is the problem in casting it to ArrayList<A> , if we can cast individual instances of B to A ?

Thanks.

The problem is this:

ArrayList<A> aList = new ArrayList<A>();
ArrayList<B> bList = new ArrayList<B>();
aList = bList; // if this were valid...
aList.add(new A()); // ...what should happen here?
B b = bList.get(0); // ...and here?

If you do the same thing with arrays, you get an ArrayStoreException in line 4 at runtime. For generic collections, it was decided to prevent that kind of thing at compile time.

Because generic are strict. they aren't covarient

ArrayList<A> aList can only refer to an ArrayList of type A


From wiki

Unlike arrays, generic classes are neither covariant nor contravariant. For example, neither List<String> nor List<Object> is a subtype of the other:

 // a is a single-element List of String List<String> a = new ArrayList<String>(); a.add("foo"); // b is a List of Object List<Object> b = a; // This is a compile-time error 

However, generic type parameters can contain wildcards (a shortcut for an extra type parameter that is only used once). Example: Given a requirement for a method which operates on Lists, of any object, then the only operations that can be performed on the object are those for which the type relationships can be guaranteed to be safe.

 // a is a single-element List of String List<String> a = new ArrayList<String>(); a.add("foo"); // b is a List of anything List<?> b = a; // retrieve the first element Object c = b.get(0); // This is legal, because we can guarantee // that the return type "?" is a subtype of Object // Add an Integer to bbadd(new Integer (1)); // This is a compile-time error; // we cannot guarantee that Integer is // a subtype of the parameter type "?" 

Wildcards can also be bound, eg " ? extends Foo " or " ? super Foo " for upper and lower bounds, respectively. This allows to refine permitted performance. Example: given a List<? extends Foo> List<? extends Foo> , then an element can be retrieved and safely assigned to a Foo type (covariance). Given a List<? super Foo> List<? super Foo> , then a Foo object can be safely added as an element (contravariance).

Animesh,

Even though class B is subtype of A, ArrayList< B > is not a subtype of ArrayList < A >. Its on the same line as in B[] is not subtype of A[]. These are two independent unrelated types.

Because there is no subtype relation in Java between C<A> and C<B> , even if A is a supertype of B or vice versa.

If your interested in the details lookup co- / contra-variance in Wikipedia.

Note, that in Java arrays are co-variant, which means A[] is a supertype of B[] if A is a supertype of B . That is the reason why you sometimes get strange cast exception with arrays.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM