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可以显式地将对象转换为A<B<C>>
,从A<?>
转换为A<B<C>>
,但不能从A<B<?>>
转换为A<B<C>>
。
为什么会这样?
答案 0 :(得分:3)
看来,您的第三个示例违反了first JLS 5.5.1 rule:
如果S是班级类型:
如果T是类类型,则为| S | &lt ;: | T |,或| T | &lt;:| S |。 否则,发生编译时错误。
此外,如果存在T的超类型X,并且存在超类型Y. S,使得X和Y都可证明是不同的参数化类型 (§4.5),并且X和Y的擦除是相同的,编译时 发生错误。
的确,让S
为List<SomeType<?>>>
而T
为List<SomeType<X>>
。这些S
和T
可以证明是不同的参数化类型,因为<?>
不等于<X>
。同时他们的删除是相同的,只是:List
和List
。
因此,根据规范,这会导致编译时错误。
当您首次投放m.get(...)
至Object
时,您没有违反上述条件:Object
和List<SomeType<X>>
没有相同的删除,此外,{{1} }
P.S。至于|List<SomeType<X>>| <: |Object|
的情况,这也不违反上述规则,因为List<?>
。
答案 1 :(得分:2)
Java不会编译可证明无法成功的类型转换(假设事物是它们被声明为的类型,并假设该值不是null
)。为了使类型转换成功,必须(理论上)可以使用非null类型作为两种类型的子类型。
Object
到A<B<C>>
:这有可能成功。例如,类型A<B<C>>
是两者的子类型。
A<?>
到A<B<C>>
:这有可能成功。例如,类型A<B<C>>
是两者的子类型。
A<B<?>>
到A<B<C>>
:这不可能成功。即不存在属于两者的子类型的类型。
要查看最后一个的原因,请回想一下,对于参数化类型,Foo<A>
如果Foo<B>
和A
不同且B
不同,则A<B<?>>
不能是B<?>
的子类型通配符。所以考虑?
。其参数? extends something
不是通配符(它是实际类型;它不是? super something
,A<B<?>>
或SubclassOfA<B<?>>
)。
因此,唯一可以是A<B<C>>
子类型的类型本身和A<B<C>>
。同样的事情也适用于SubclassOfA<B<C>>
:唯一可以是{{1}}子类型的类型本身和{{1}}。
那么你能看到不可能有一个两种类型的类型吗?
答案 2 :(得分: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允许从Object
到List<Class<Boolean>>
以及从List<?>
到List<Class<Boolean>>
进行投射,但两者都会产生未经检查的警告。它们不是错误,因为它们可以被认为是类型安全的,而List<Class<?>>
到List<Class<Boolean>>
不能是类型安全的。
答案 3 :(得分:0)
此
public static <X> List<SomeType<X>> getList(Class<X> clazz)
{
return (List<SomeType<X>>)m.get(clazz);//will not compile
}
不允许这样做,因为编译器对SomeType一无所知。
您已创建一个类型参数为X
且参数为Class的方法,但结果您希望返回一个存储SomeType
X
类型的List。您没有指定如何从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 ;-)
}