繁体   English   中英

为什么Java允许将Object类型的表达式显式转换为A <B<C> &gt;,类型A <?> 到A <B<C> &gt;但不是A型 <B<?> &gt;至A <B<C> &gt;?

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

Java将允许我这样做:

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
}

它还可以让我做到这一点:

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
}

但这不会让我这样做:

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
}

除非我采用以下解决方法:

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
}

因此,java使得可以将从显式转换为Object到A<B<C>> ,从A<?>A<B<C>>但不能从A<B<?>>A<B<C>>

这是为什么?

看来,您的第三个示例违反了第一个JLS 5.5.1规则

如果S是类类型:

如果T是类类型,那么| S | <:| T |或| T | <:| S |。 否则,将发生编译时错误。

此外,如果存在T的超类型X和S的超类型Y,以致X和Y都是可证明是截然不同的参数化类型(第4.5节),并且X和Y的擦除相同,则编译时发生错误。

实际上,令SList<SomeType<?>>>TList<SomeType<X>> 这些ST是可证明是截然不同的参数化类型,因为<?>不等于<X> 同时,它们的擦除相同,即: ListList

因此,根据规格,这会导致编译时错误。

当您第一次将m.get(...)Object ,您不会违反提到的条件: ObjectList<SomeType<X>>没有相同的擦除,此外, |List<SomeType<X>>| <: |Object| |List<SomeType<X>>| <: |Object|

PS对于List<?>情况,这也没有违反提到的规则,因为|List<SomeType<X>>| <: |List<?>| |List<SomeType<X>>| <: |List<?>|

Java不会编译可证明无法成功的类型转换 (假设事物是它们被声明为的类型,并假定该值不为null )。 为了使类型转换成功,从理论上讲,必须有一个非空类型,它是这两种类型的子类型。

  • ObjectA<B<C>> :这是可能的这一成功。 例如,类型A<B<C>>是两者的子类型。

  • A<?>A<B<C>> :这有可能成功。 例如,类型A<B<C>>是两者的子类型。

  • A<B<?>>A<B<C>> :不可能成功。 即,不能存在作为两者的子类型的类型。

要了解为什么要使用最后一个,请回想一下对于参数化类型,如果AB不同,并且Foo<A>都不是Foo<B>的子类型,并且都不是通配符。 因此,考虑A<B<?>> 它的参数B<?>不是通配符(它是实际类型;它不是?? extends something? super something )。

因此,唯一可以作为A<B<?>>子类型的类型是本身,以及SubclassOfA<?B<?>> 同样的情况也适用于A<B<C>> :唯一可以成为A<B<C>>的子类型的类型本身就是SubclassOfA<?B<C>>

那么,您能否看到不可能同时拥有两者的子类型呢?

因为它不是类型安全的。 一个例子:

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);

此外,尽管Java允许从ObjectList<Class<Boolean>>的强制转换以及从List<?>List<Class<Boolean>>的强制转换,但两者都会产生未经检查的警告。 它们不是错误,因为可以想象它们是类型安全的,而List<Class<?>>List<Class<Boolean>>不能是类型安全的。

这个

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

不允许这样做是因为,编译器对SomeType一无所知。

您已经创建了带有类型参数X和参数Class的方法,但是结果是您期望返回一个存储X类型的SomeType的列表。 您没有指定如何从X传递到SomeType。 获取期望Object的映射,但返回声明的类型的女巫是SomeType<?>

作为? 在这种情况下,它与X更不一样,您可以执行以下操作。

为了解决这个问题,如果要在返回类型中使用它,则必须说X必须是。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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