📜  Java泛型-通配符使用准则(1)

📅  最后修改于: 2023-12-03 14:43:01.748000             🧑  作者: Mango

Java泛型-通配符使用准则

Java泛型中的通配符是一种非常强大的工具,它允许我们编写泛型代码,其中类型参数可以是任何类型。 但是,通配符也是容易出错的:使用不当会导致编译时或运行时错误。 在本文中,我们将讨论通配符的使用准则,以便程序员可以更好地应用它们来编写优秀的Java泛型代码。

通配符简介

Java中的通配符用于表示未知类型。通配符用'?' 表示,我们可以使用它来代替泛型类型定义的实际类型参数。

一个最简单的通配符使用如下:

List<?> numList = new ArrayList<>();

这里的“?”表示未知类型。

通配符的用处
  • 最大化代码重用:通配符可以实现通用的泛型代码,这使得代码更容易重用和维护。

  • 避免类型转换:使用通配符可以避免必须将对象强制转换为与特定泛型类型一致。

  • 改善代码可读性:通配符可为贡献者提供更清晰的接口。

extends子句

Java中的通配符具有一个称为"extends子句"的属性。extends子句是一个限定,它允许我们定义通配符可以接受的类型参数的范围。

下面是一个使用extends子句限定泛型类型的示例:

List<? extends Number> nums = new ArrayList<>();

这里表示该列表接受:Number以及Number的任何子类。

super子句

Java中的通配符也具有一个称为"super子句"的属性。super子句是一个限制,它允许我们把通配符限制为接受超类型的参数。

下面是使用super子句限制泛型类型的示例:

List<? super Integer> nums = new ArrayList<>();

这表示该列表接受Integer以及任何Integer的超类型。

?和T的区别

在泛型代码中,我们通常会看到两种类型占位符:?和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与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泛型中,通配符是一种强大的工具,它使我们能够编写通用的泛型代码。 但是,如果我们不小心,通配符也可能导致错误。 在使用通配符时,请始终记住以下规则:

  • 不要在需要精确类型的情况下使用通配符。

  • 不要使用通配符作为返回类型。

  • 如果需要读取和写入泛型类型,则使用类型参数,而不是通配符。