我偶然发现了一段让我想知道为什么它编译成功的代码:
public class Main {
public static void main(String[] args) {
String s = newList(); // why does this line compile?
System.out.println(s);
}
private static <T extends List<Integer>> T newList() {
return (T) new ArrayList<Integer>();
}
}
有趣的是,如果我修改方法的签名 newList
与 <T extends ArrayList<Integer>>
它不再起作用了。
评论和回复后更新: 如果我将泛型类型从方法移动到类,则代码不再编译:
public class SomeClass<T extends List<Integer>> {
public void main(String[] args) {
String s = newList(); // this doesn't compile anymore
System.out.println(s);
}
private T newList() {
return (T) new ArrayList<Integer>();
}
}
最佳答案
如果您在方法中声明类型参数,则您允许调用者为其选择实际类型,只要该实际类型满足约束条件。该类型不必是实际的具体类型,它可能是抽象类型、类型变量或交集类型,换句话说,更通俗地说,是假设类型。所以,as said by Mureinik ,可能有一个类型扩展 String
并实现 List
。我们无法手动为调用指定交集类型,但可以使用类型变量来演示逻辑:
public class Main {
public static <X extends String&List<Integer>> void main(String[] args) {
String s = Main.<X>newList();
System.out.println(s);
}
private static <T extends List<Integer>> T newList() {
return (T) new ArrayList<Integer>();
}
}
当然,newList()
不能满足返回这样一个类型的期望,但这就是这个方法的定义(或实现)的问题。将 ArrayList
转换为 T
时应该会收到“未检查”警告。唯一可能的正确实现是在此处返回 null
,这会使该方法毫无用处。
重复最初的陈述,重点是泛型方法的调用者为类型参数选择实际类型。相反,当你声明一个通用的 class 像 with
public class SomeClass<T extends List<Integer>> {
public void main(String[] args) {
String s = newList(); // this doesn't compile anymore
System.out.println(s);
}
private T newList() {
return (T) new ArrayList<Integer>();
}
}
类型参数是类契约的一部分,因此创建实例的人将为该实例选择实际类型。实例方法 main
是该类的一部分,并且必须遵守该约定。你不能选择你想要的T
; T
的实际类型已经设置,在 Java 中,你通常甚至无法找出 T
是什么。
泛型编程的关键是编写独立于为类型参数选择的实际类型工作的代码。
但请注意,您可以创建 另一个,具有您喜欢的任何类型的独立实例并调用该方法,例如
public class SomeClass<T extends List<Integer>> {
public <X extends String&List<Integer>> void main(String[] args) {
String s = new SomeClass<X>().newList();
System.out.println(s);
}
private T newList() {
return (T) new ArrayList<Integer>();
}
}
在这里,新实例的创建者选择该实例的实际类型。如前所述,实际类型不需要是具体类型。
关于java - 为什么这个通用代码在 java 8 中编译?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38092697/