4

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 可以显式转换 from to Object to A<B<C>>, from A<?>toA<B<C>>但不是 from A<B<?>>to A<B<C>>

这是为什么?

4

4 回答 4

3

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

如果 S 是类类型:

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

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

的确,让S成为List<SomeType<?>>>T成为List<SomeType<X>>。这些ST可证明是不同的参数化类型,因为<?>不等于<X>。同时它们的擦除是相同的,只是:ListList

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

当您第一次m.get(...)转换为 时Object,您不违反上述条件:Object并且List<SomeType<X>>没有相同的擦除,此外,|List<SomeType<X>>| <: |Object|

PS 至于List<?>案例,这也不违反上述规则,因为|List<SomeType<X>>| <: |List<?>|.

于 2013-07-16T15:19:27.477 回答
2

Java 不会编译可能无法成功的类型转换(假设事物是它们被声明为的类型,并假设值不是null)。为了使类型转换能够成功,它必须(理论上)可能具有作为两种类型的子类型的非空类型。

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

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

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

要了解最后一个的原因,请回想一下,对于参数化类型,Foo<A>不能是Foo<B>if的子类型,A并且B是不同的,也不是通配符。所以考虑A<B<?>>。它的参数B<?>不是通配符(它是实际类型;它不是?? extends something? super something)。

所以唯一可以成为子类型的类型A<B<?>>是它自己,并且SubclassOfA<B<?>>. 同样的事情适用于A<B<C>>:唯一可以是子类型的类型A<B<C>>是它自己,并且SubclassOfA<B<C>>.

所以你能看到不可能有一个类型是两者的子类型吗?

于 2013-07-18T03:13:20.487 回答
1

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

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 将允许从ObjecttoList<Class<Boolean>>和 from List<?>to强制转换List<Class<Boolean>>,但两者都会产生未经检查的警告。它们不是错误,因为可以想象它们是类型安全的,而List<Class<?>>toList<Class<Boolean>>不能是类型安全的。

于 2013-07-16T15:16:16.423 回答
0

这个

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

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

您已经创建了一个具有类型参数X和参数类的方法,但结果您希望返回一个类型存储SomeType的列表X。您没有指定如何从 X 传递到 SomeType。get 中的 map Object,但返回声明的类型 witch is SomeType<?>

由于?在这种情况下与 更不一样X,你可以做这样的事情。

要解决这个问题,你需要说,如果你想在返回类型中使用它,那 X 必须是。

public static <X extends SomeType<?>> List<? super X> getList(Class<X> clazz)
{
    return  m.get(clazz); //No error, no warring ;-)
}
于 2013-07-16T15:26:26.653 回答