这是我之前的问题的后续,但由于前一个线程很长,我决定启动另一个与几乎相同主题相关的线程.
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
}
Run Code Online (Sandbox Code Playgroud)
注意:如果将光标移到每个子句上,您将看到在Eclipse上生成和显示的推理:
一个.条款(1)将产生<?extends Object> test1 <?extends Object,?extends Object>
b.第(2)款将完全产生实际类型参数
c中定义的内容.第(3)条将产生<Object> test3 <Object,List <Object >>
问题:
为什么子句(1)没有产生<Object>?既然<Object>的工作原理如第(2)条所示,为什么<? 扩展对象>被生产代替?
这是三个问题中最好的一个。我的想法是,编译器/Eclipse 不想假设必然是和之间推断的Object
类型,因此它会谨慎行事。正如@bringer128指出的,并且都实现了- 所以这些类型也是方法的推断类型的候选类型。T
String
Integer
String
Integer
Serializable
Comparable
值得注意的是,以下代码给出了编译器错误“非法类型开始”:
GenericMethodInference.<? extends Object>test1("Hello", new Integer(1));
Run Code Online (Sandbox Code Playgroud)
这是因为将通配符指定为方法的类型参数是无效的。因此,您在工具提示中看到的事实与编译器/Eclipse 报告此信息的功能的微妙有关 - 它仅确定了在T
其范围内的信息,而不是确定了它是什么。
请记住,Java 的泛型实现只是为了程序员的方便/理智。一旦编译成字节码,类型擦除将消除任何T
. 因此,在检查时,编译器只需要确保可以推断出有效的值,但不一定可以推断出它是什么。T
为什么子句 (3) 产生 <Object> 而不是 <?扩展对象>?
List<Object>
因为在这种情况下,将 a传入预期 a 的位置这一事实List<T>
告诉编译器这T
正是Object
。
既然第(4)条使用了相同的变量,为什么即使使用的参数是相同的变量d,也会生成2个不同类型的捕获?
编译器假设d
实际上引用同一个对象是不安全的,即使在评估参数之间也是如此。例如:
test4(d,(d = new ArrayList<String>()));
Run Code Online (Sandbox Code Playgroud)
在这种情况下,aList<Integer>
将被传递到第一个参数,an 将List<String>
被传递到第二个参数 - 两者都来自d
. 由于这种情况是可能的,因此编译器更容易安全行事。