为什么不能将嵌套通配符分配给带有类型参数的变量?

时间:2018-04-06 17:27:16

标签: java generics

考虑这个例子:

static class Generic<E> {}

static void run() {
    Generic<?> x = null;
    take(x);
}

static <E> void take(final Generic<E> x) {}

run中,x类型的通配符代表某种未知类型T。可以为E方法的take参数指定任何类型。编译器推断将T分配给E是安全的。

在第二个例子中,我们将通配符嵌套一层更深:

static void run() {
    Generic<Generic<?>> x = null;
    take(x);
}

static <E> void take(final Generic<Generic<E>> x) {}

在此示例中,通配符仍表示某种未知类型T,并且可以为E参数指定任何类型。为什么编译器不推断可以将T分配给E并允许编译该代码?这段代码是否有原则上没有编译?

注意:将第二个示例中的take方法更改为以下编译:

static <E> void take(final Generic<Generic<? extends E>> x) {}

Generic<Generic<E>>无界时,Generic<Generic<? extends E>>E之间是否存在本质区别?

2 个答案:

答案 0 :(得分:3)

对于未知Generic<Generic<?>>

Generic<Generic<T>>并不代表T。它表示特定类型Generic<Generic<?>>。对于任何Generic<Generic<?>>,类型为Generic<Generic<T>>的对象永远不会是T类型。

例如,如果您有List<List<?>>,则可以使用List<String>List<Integer>List<Object>等作为元素的列表。 T没有List<List<T>>可以执行此操作的类型{。}}。

直观地说,第一个代码段会针对某些未知的take<T>调用T,而第二个代码段则需要使用take<?>作为类型参数专门调用?,这是禁止。具体来说,我认为Java为capture conversion ?中的Generic<?>为第一个片段生成了一个新的类型变量,而第二个片段中的嵌套通配符不会发生这种变量。尽管如此,我还没有完全弄清楚Java Language Specification如何说出类型推断。

答案 1 :(得分:0)

我不确定,但我认为这也适用于Liskov原则。泛型类型是通过类型参数化的泛型类或接口。 Liskov替换原则不适用于参数化类型。 例如,如果你写的话是编译时错误:

List<Numbers> list = new ArrayList<Integers>();

这同样适用于通配符:在使用Generic<Generic<E>>评估Generic<Generic<?>>时,Liskov原则会告诉您Generic<E>Generic<?>没有子类型关系。这就是它给出编译时错误的原因,因为它不能从中推断出什么?与E。