📅  最后修改于: 2023-12-03 14:43:01.748000             🧑  作者: Mango
Java泛型中的通配符是一种非常强大的工具,它允许我们编写泛型代码,其中类型参数可以是任何类型。 但是,通配符也是容易出错的:使用不当会导致编译时或运行时错误。 在本文中,我们将讨论通配符的使用准则,以便程序员可以更好地应用它们来编写优秀的Java泛型代码。
Java中的通配符用于表示未知类型。通配符用'?' 表示,我们可以使用它来代替泛型类型定义的实际类型参数。
一个最简单的通配符使用如下:
List<?> numList = new ArrayList<>();
这里的“?”表示未知类型。
最大化代码重用:通配符可以实现通用的泛型代码,这使得代码更容易重用和维护。
避免类型转换:使用通配符可以避免必须将对象强制转换为与特定泛型类型一致。
改善代码可读性:通配符可为贡献者提供更清晰的接口。
Java中的通配符具有一个称为"extends子句"的属性。extends子句是一个限定,它允许我们定义通配符可以接受的类型参数的范围。
下面是一个使用extends子句限定泛型类型的示例:
List<? extends Number> nums = new ArrayList<>();
这里表示该列表接受:Number以及Number的任何子类。
Java中的通配符也具有一个称为"super子句"的属性。super子句是一个限制,它允许我们把通配符限制为接受超类型的参数。
下面是使用super子句限制泛型类型的示例:
List<? super Integer> nums = new ArrayList<>();
这表示该列表接受Integer以及任何Integer的超类型。
在泛型代码中,我们通常会看到两种类型占位符:?和T。?.是用于通配符,而T是用于类型参数。
T被用来指示特定类型的成员变量,方法参数,返回类型等等,可以在类、方法中定义。
区分这两者是很重要的,?表示任何类型,而T表示一种特定类型。
public static <T> T getFirst(List<? extends T> list) {
return list.get(0); // Compiler error!
}
上面的代码将返回一个未知类型的值,这是不必要的。正确的方式是使用类型参数:
public static <T> T getFirst(List<T> list) {
return list.get(0);
}
使用通配符作为返回类型是不允许的,因为它不能保证返回类型与方法调用时的预期类型匹配。
public static List<?> getList() { // Compiler error!
return Collections.emptyList();
}
正确的方式是使用类型参数:
public static <T> List<T> getList() {
return Collections.emptyList();
}
如果我们需要读取和写入集合元素,则使用类型参数是最好的方式,而不是使用通配符。
public static <T> void consume(List<T> list) {
for (T elem : list) {
System.out.println(elem);
}
}
public static void main(String[] args) {
List<String> list = Arrays.asList("foo", "bar");
consume(list);
}
如果我们想编写集合,则更改List
public static <T> List<T> produce() {
return Arrays.asList((T) "foo", (T) "bar");
}
public static void main(String[] args) {
List<String> list = produce();
consume(list);
}
在Java泛型中,通配符是一种强大的工具,它使我们能够编写通用的泛型代码。 但是,如果我们不小心,通配符也可能导致错误。 在使用通配符时,请始终记住以下规则:
不要在需要精确类型的情况下使用通配符。
不要使用通配符作为返回类型。
如果需要读取和写入泛型类型,则使用类型参数,而不是通配符。