这是我上一个问题的后续内容,但由于前一个帖子很长,我决定启动另一个与几乎相同主题有关的线程。
public class GenericMethodInference {
static <T> void test1(T t1, T t2) {}
static <T> void test3(T t1, List <T> t2) {}
static <T> void test4(List <T> t1, List <T> t2) {}
public static void main(String [] args) {
List <Object> c = new LinkedList<Object>();
List <? extends Object> d = new ArrayList<Integer>();
List e = new ArrayList<Integer>();
test1("Hello", new Integer(1)); // ok clause (1)
GenericMethodInference.<Object>test1("Hello", new Integer(1)); // ok clause (2)
test3("Hello", c); // ok clause (3)
test4(d,d) // clause (4) Error due to different type capture generated
}
注意:如果将光标移到每个子句上,您将看到在Eclipse上生成和显示的推理:
一个。条款(1)将产生&lt;? extends Object&gt; test1&lt;? extends Object,? extends Object&gt;
湾第(2)款将完全产生实际类型参数中定义的内容
C。条款(3)将产生&lt; Object&gt; test3&lt; Object,List&lt; Object&gt;&gt;
问题:
答案 0 :(得分:4)
为什么第(1)条没有产生&lt; Object&gt;?由于&lt; Object&gt;如第(2)项所示,为什么&lt;? extends Object&gt;正在生产?
这是三个中最好的问题。我的想法是编译器/ Eclipse不想假设Object
必然是在T
和String
之间推断的类型Integer
,所以它可以安全地运行。正如@bringer128所指出的那样,String
和Integer
也都实现了Serializable
和Comparable
- 因此这些类型也是方法的推断类型的候选者。 / p>
值得注意的是,以下代码给出了编译器错误“非法启动类型”:
GenericMethodInference.<? extends Object>test1("Hello", new Integer(1));
这是因为将通配符指定为方法的类型参数是无效的。因此,您在工具提示中看到的事实与编译器/ Eclipse的工具的细微之处有关,以报告此信息 - 它只确定T
在其范围内,而不是它的范围。
请记住,Java的泛型实现仅仅是为了程序员的方便/理智。一旦编译成字节码,type erasure就会摆脱T
的任何概念。因此,在检查时,编译器只需要确保可以推断出 a 有效T
,但不一定是这样。
为什么第(3)条产生&lt; Object&gt;而不是&lt;? extends Object&gt;?
因为在这种情况下,预期List<Object>
传递List<T>
的事实告诉编译器T
正是Object
。
由于第(4)条使用相同的变量,为什么2个不同类型的捕获产生的事件尽管使用的参数是相同的变量d?
编译器认为d
实际引用同一个对象是不安全的,即使在评估参数之间也是如此。例如:
test4(d,(d = new ArrayList<String>()));
在这种情况下,List<Integer>
将传递到第一个参数,而List<String>
将传递到第二个参数 - 均来自d
。由于这种情况是可能的,因此编译器更容易安全地使用它。
答案 1 :(得分:2)
test1()案件实际上非常险恶。见JLS3 15.12.2.7。
我们不应该知道类型推断的细节 - 在大多数情况下,直觉与算法一致。唉,情况并非总是如此,如看似简单的test1()示例。
我们的约束是T :> String
和T :> Integer
(“:>
”表示超类型)
这导致T=lub(String,Integer)
,lub
表示“最小上限”。
自String <: Comparable<String>
和Integer <: Comparable<Integer>
后,这会产生lci({Comparable<String>, Comparable<Integer>})
,产生Comparable<? extends lub(String,Integer)>
,即Compable<? extends T>
最后,我们有T = Serializable & Compable<? extends T>
,一个自我引用的定义! Spec称之为“无限型”:
上述过程可能会产生无限类型。这是允许的,Java编译器必须识别这种情况并使用循环数据结构恰当地表示它们。
让我们看看javac如何表示它:(javac 7)
static <T> T test1(T t1, T t2) {}
public static void main(String[] args)
{
Void x = test1("Hello", new Integer(1));
}
error: incompatible types
required: Void
found: INT#1
where INT#1,INT#2 are intersection types:
INT#1 extends Object,Serializable,Comparable<? extends INT#2>
INT#2 extends Object,Serializable,Comparable<?>
这似乎不对;它不是真正的递归;似乎javac在lub()
中检测到递归并放弃,从而产生一个不太具体的类型Comparable<?>