考虑以下示例:
public class Learn {
public static <T> T test (T a, T b) {
System.out.println(a.getClass().getSimpleName());
System.out.println(b.getClass().getSimpleName());
b = a;
return a;
}
public static void main (String[] args) {
test("", new ArrayList<Integer>());
}
}
在main
方法中,我使用test
和String
对象调用ArrayList <Integer>
。两者都是不同的东西,并将ArrayList
分配给String
(通常)会产生编译错误。
String aString = new ArrayList <Integer> (); // won't compile
但是我在test
的第3行正是这样做的,程序编译并运行正常。首先,我认为类型参数T
被替换为与String
和ArrayList
兼容的类型(如Serializable
)。但println
内的两个test
语句分别将“{1}}和a
打印为”{1}}和“{{}}”。我的问题是,如果b
在运行时a
而String
为b
,我们如何将ArrayList
分配给a
。
答案 0 :(得分:10)
对于通用方法,Java编译器a
和b
都将infer the most specific common type。
推理算法确定参数的类型,如果可用,还确定分配或返回结果的类型。最后,推理算法试图找到适用于所有参数的特定类型。
您没有将test
的调用结果分配给任何内容,因此没有影响推理的目标。
在这种情况下,即使String
和ArrayList<Integer>
也有一个共同的超类型Serializable
,因此T
被推断为Serializable
,您始终可以指定一个相同类型的变量到另一个变量。对于其他示例,您甚至可以将Object
视为常见的超类型。
但仅仅因为你有T
类型的变量推断为Serializable
,对象本身仍然是String
和ArrayList
,所以得到他们的类和打印他们的名字仍会打印String
和ArrayList
。你不打印变量的类型;你正在打印对象的类型。
答案 1 :(得分:4)
test("", new ArrayList<Integer>());
这相当于以下内容:
Learn.test("", new ArrayList<Integer>());
这也等同于以下内容:
Learn.<Serializable>test("", new ArrayList<Integer>());
如果您明确指定Serializable
(或Object
)以外的泛型类型,例如String
,则无法编译:
Learn.<String>test("", new ArrayList<Integer>()); // DOES NOT COMPILE
因此,在您的情况下,基本上两个参数都被视为Serializable
。