简体   繁体   中英

JLS as related to type casting

I try to understand more fundamentally type casting in Java but can not understand some parts of JLS.
In particular this (in the meaning of casting of class type S to class or interface type T):

Furthermore, if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types (§4.5), and that the erasures of X and Y are the same, a compile-time error occurs.

and this (in the meaning of casting of interface type S to final class type T):

Otherwise, S is either a parameterized type that is an invocation of some generic type declaration G, or a raw type corresponding to a generic type declaration G. Then there must exist a supertype X of T, such that X is an invocation of G, or a compile-time error occurs. Furthermore, if S and X are provably distinct parameterized types then a compile-time error occurs.

Maybe someone can give some brief examples to clarify these extracts?

PS After a some consideration and guidance from @ErickGHagstrom I think I can give a clarification of these two tricky JLS extracts.

Extract 1.

JLS says:

Two parameterized types are provably distinct if either of the following is true:

  • They are parameterizations of distinct generic type declarations.

  • Any of their type arguments are provably distinct.

and:

Two type arguments are provably distinct if one of the following is true:

  • Neither argument is a type variable or wildcard, and the two arguments are not the same type.

  • One type argument is a type variable or wildcard, with an upper bound (from capture conversion (§5.1.10), if necessary) of S; and the other type argument T is not a type variable or wildcard; and neither |S| <: |T| nor |T| <: |S| (§4.8, §4.10).

  • Each type argument is a type variable or wildcard, with upper bounds (from capture conversion, if necessary) of S and T; and neither |S| <: |T| nor |T| <: |S|.

Therefore List<Integer> and List<Number> are provably distinct but List<? extends Integer> List<? extends Integer> and List<? extends Number> List<? extends Number> are not (all listed types have the same erasure).

The key point is that sub-typing relationship between two provably distinct parametrized types with the same erasure is not possible in any way.

Sub-typing relationship between sub-types of two provably distinct parametrized types with the same erasure is also not possible in any way. If S <: List<Integer> and T <: List<Number> then castings (T)S and (S)T both are not possible even theoretically. So compiler complains.

Extract 2.

Here is the example I came up with (compiles and runs without errors):

    static final class T extends ArrayList<Number>{
    }

    static T t;

    static List<?> l1 = new T();
    static List<? extends Number> l2 = new T();
    static List<String> l3;

    public static void main(String[] args) {
        t = (T)l1;
        t = (T)l2;
//        t = (T)l3; //error
    }

You can see that List<?> and List<? extends Number> List<? extends Number> can be cast to final T while T does not (and can not) implement any of these interfaces.

T <: List<Number> . List<Number> is invocation of List<E> like List<?> and List<? extends Number> List<? extends Number> . List<Number> and List<?> and List<? extends Number> List<? extends Number> are not provably distinct (see above).

If we want to cast something of parametrized interface type S to final class type T this T need not explicitly or implicitly to implement S . But this T must to implement some another parametrized interface type which is in sub-type relationship with S . The key point is that sub-type relationship between parametrized types is defined not only by extending or implementing but also by type argument containing .

The first situation:

ArrayList<String> vs. ArrayList<Integer> have the same erasure, ArrayList , but are provably distinct because no String can ever be cast to an Integer and no Integer can ever be cast to a String .

The second situation:

Let G be List<T> and S be List<String> . Suppose I have a class MyFinalArrayList like so:

public final class MyFinalIntArrayList extends ArrayList<Integer> {
    ...
}

and another like

public final class MyFinalStrArrayList extends ArrayList<String> {
    ...
}

I can cast the first to List<Integer and the second to List<String , but not vice versa.

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