Why does Java allow explicit conversion of expressions of type Object to <B <C>>, type A <?> To A <B <C>>, but not type A <B <? >> to A <B <C>>?

Java will let me do this:

public static class SomeType<I>{} private static Map<Class<?>, Object> m = new HashMap<Class<?>, Object>(); public static <X> List<SomeType<X>> getList(Class<X> clazz) { return (List<SomeType<X>>)m.get(clazz);//warning } 

This will also allow me to do this:

 public static class SomeType<I>{} private static Map<Class<?>, List<?>> m = new HashMap<Class<?>, List<?>>(); public static <X> List<SomeType<X>> getList(Class<X> clazz) { return (List<SomeType<X>>)m.get(clazz);//warning } 

But this will not allow me to do this:

 public static class SomeType<I>{} private static Map<Class<?>, List<SomeType<?>>> m = new HashMap<Class<?>, List<SomeType<?>>>(); public static <X> List<SomeType<X>> getList(Class<X> clazz) { return (List<SomeType<X>>)m.get(clazz);//will not compile } 

if I do not resort to the following workaround:

 public static class SomeType<I>{} private static Map<Class<?>, List<SomeType<?>>> m = new HashMap<Class<?>, List<SomeType<?>>>(); public static <X> List<SomeType<X>> getList(Class<X> clazz) { return (List<SomeType<X>>)(Object)m.get(clazz);//warning } 

Thus, java allows you to explicitly convert from Object to A<B<C>> , from A<?> To A<B<C>> , but not from A<B<?>> to A<B<C>> .

why?

+4
source share
4 answers

Java will not compile casts of types that may not be successful (assuming that things are the type that they are declared as, and assuming that this value is not null ). In order for a type cast to be successful, it must be (theoretically) possible to have a non-empty type that is a subtype of both types.

  • Object to A<B<C>> : This is possible for success. For example, type A<B<C>> is a subtype of both.

  • A<?> To A<B<C>> : This is possible for success. For example, type A<B<C>> is a subtype of both.

  • A<B<?>> to A<B<C>> : This is not possible for success. that is, a type that is a subtype of both cannot exist.

To find out why for the latter, recall that for parameterized types, Foo<A> cannot be a subtype of Foo<B> if A and B are different from each other and are not wildcards. Therefore, we consider A<B<?>> . Its parameter B<?> Is not a wildcard (it is the actual type, it is not ? , ? extends something , or ? super something ).

So, the only types that can be a subtype of A<B<?>> are themselves, and SubclassOfA<B<?>> . The same applies to A<B<C>> : only types that can be a subtype of A<B<C>> , by themselves, and SubclassOfA<B<C>> .

So you can see how impossible it is to have a type that is a subtype of both?

+2
source

Your third example seems to violate the first rule of JLS 5.5.1 :

If S is a class type:

If T is a class type, then either | S | <: | T | or | T | <: | S |. Otherwise, a compile-time error occurs.

In addition, if there is a supertype X of T and a supertype Y of S, so that both X and Y are predictable different parameterized types (Β§4.5), and that the erasures of X and Y are the same, compilation time fails.

Indeed, let S be List<SomeType<?>>> and T be List<SomeType<X>> . These S and T are clearly different parameterized types because <?> Is not equal to <X> . At the same time, their erasures are the same, simply: List and List .

Thus, according to the specifications, this leads to a compile-time error.

When your first listing is m.get(...) before Object , you do not violate the specified condition: Object and List<SomeType<X>> do not have the same erasures, and, in addition, |List<SomeType<X>>| <: |Object| |List<SomeType<X>>| <: |Object|

PS as for the case List<?> , This also does not violate the indicated rule, because |List<SomeType<X>>| <: |List<?>| |List<SomeType<X>>| <: |List<?>| .

+3
source

Because it is not type safe. Example:

 List<Class<?>> classes = new ArrayList<Class<?>>(); classes.add(String.class); // This is invalid! List<Class<Boolean>> casted = (List<Class<Boolean>>) classes; // We've somehow assigned a Class<String> to a Class<Boolean>! Class<Boolean> invalid = casted.get(0); 

In addition, while Java will allow dropping from Object to List<Class<Boolean>> and from List<?> To List<Class<Boolean>> , both will give an unchecked warning. They are not errors, because they can be type safe, while List<Class<?>> - List<Class<Boolean>> cannot be type safe.

+1
source

it

 public static <X> List<SomeType<X>> getList(Class<X> clazz) { return (List<SomeType<X>>)m.get(clazz);//will not compile } 

This is not allowed because the compiler knows nothing about SomeType.

You have created a method with a parameter of type X and a parameter of Class, but as a result, you expect to return a list in which SomeType type X is stored. You did not specify how to switch from X to SomeType. The map in get expect Object , but return the declared type witch SomeType<?> .

Since ? in this case it is no different from X , you can do something like this.

To fix this, you need to say that it must be X if you want to use it in the inverse type.

 public static <X extends SomeType<?>> List<? super X> getList(Class<X> clazz) { return m.get(clazz); //No error, no warring ;-) } 
0
source

Source: https://habr.com/ru/post/1491674/


All Articles