1. 什么是通配符
通配符是一种泛型技术,它可以接受不同类型的参数,而不需要在编译时指定具体的类型。通配符使用符号 "?" 表示,它表示一个不确定的类型参数,可以用于方法的参数列表,类的定义,方法返回值等。
通配符可以用于以下两种类型:
- 上界通配符 (Upper Bound Wildcards)
- 下界通配符 (Lower Bound Wildcards)
2. 上界通配符
上界通配符用于限制泛型参数的类型范围。它可以指定泛型参数必须是某个类型的子类或实现了某个接口。
例如,假设我们要编写一个方法,求一个列表中所有元素的和,那么可以使用上界通配符,限定列表元素类型必须是 Number 或 Number 的子类:
public static double sum(List<? extends Number> list) {...}
这样,我们就可以传入 List<Integer>、List<Double> 等泛型参数进行计算。
因为 list 列表的元素类型都是 Number 或 Number 的子类,所以当我们使用 Number 类型来进行计算时,可以保证类型安全。
Java 常用库中使用上界通配符的方法有:Collections.max、Collections.min 等等。
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
该方法用于计算集合中的最大值,并从泛型集合中确定当前 T 类型参数的范围,即泛型类型参数必须是实现了 Comparable 接口的类。
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
int max = Collections.max(intList); // 返回 5
上面例子中,intList 中的元素都是 Integer 类型,是 Number 的子类,因此可以直接传递给 Collections.max 方法使用。
3.下界通配符
下界通配符则是用于限制泛型参数的类型范围,但是它与上界通配符的限制方向相反。它可以指定泛型参数必须是某个类型的父类或所实现的某个接口的父接口。
例如,假设我们需要编写一个方法,将一个列表中的元素复制到另一个列表中。然后我们可以使用下界通配符,限定列表元素类型必须是 Object 或 Object 的子类:
public static void copy(List<? super Object> dest, List<Object> src) {...}
这样,我们就可以传入 List<Object>、List<Number>、List<String> 等泛型参数进行复制。
因为 dest 列表的元素类型都是 Object 或 Object 的父类,所以当我们复制 src 到 dest 时,可以保证类型安全。
Java 常用库中使用下界通配符的方法有:Collections.addAll 、Collections.copy 等等。
public static <T> boolean addAll(Collection<? super T> c, T... elements)
该方法用于将指定元素添加到集合中,并且判断添加操作是否成功。其中,c 是泛型集合类型,而 T 是泛型类型参数:
List<Number> numberList = new ArrayList<>();
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
Collections.addAll(numberList, 1, 2.0, 3f);
在上面的例子中,numberList 的类型参数是 Number,它是 Integer 的父类,因此我们可以直接将 List<Integer> 类型的 intList 作为参数传递给 Collections.addAll 方法。
4.无界通配符
如果我们不确定通配符要用到什么类型,或者我们只对类型是任何类的参数感兴趣,此时可以使用无界通配符 <?>。这种情况下,使用者只需传递任意类型的参数即可。
举个例子,假设有一个方法需要将 List 中所有元素都打印出来:
public static void printList(List<?> list) {
for (Object item : list) {
System.out.print(item + " ");
}
}
在上面的例子中,List<?> 中包含了类型为 Object 或其任何子类的项,我们可以将 List<String>、List<Integer>、List<Double> 等任何类型的 List 作为参数传递给此方法。
List<String> strings = Arrays.asList("hello", "world");
List<Integer> integers = Arrays.asList(1, 2, 3);
printList(strings); // 输出 hello world
printList(integers); // 输出 1 2 3
5. 泛型与通配符
当我们谈论Java中的泛型时,我们指的是在定义类或方法时使用类型参数来表示一种“类型的占位符”。通配符则是在使用泛型时,用于表示具体类型的一种方式。
具体来说,泛型是用来增加类型安全性和重用性的一种机制。它允许我们在定义类或方法时指定一种“类型的占位符”,在使用这个类或方法时再指定具体的类型。例如,我们可以定义一个List
类,其中的E
表示列表中的元素类型,然后在使用这个类时指定具体的类型,如List<Integer>
或List<String>
。
而通配符,或者说通配类型,是用来表示一种不确定的或未知的类型。它用问号(?)表示。通配符可以用在泛型类、泛型方法、通配类型参数上,表示可以接受任意类型的参数。
我们可以用一个简单的例子来说明。
假设我们有一个Container
类,其中有一个getValue
方法用于从容器中获取值。如果我们只是知道这个容器中存储了某种类型的数据,但不知道具体是哪种类型的数据,我们可以使用通配符来表示这个未知的类型。
public class Container<T> {
private T value;
public Container(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
现在,我们可以创建一个容器,用一个未知的类型来存储值:
Container<?> container = new Container<>(42);
在这个例子中,<?>
就是通配符,表示了一个未知的类型。我们可以接受任何类型的容器对象。但是,我们不能直接修改这个容器中的值,因为我们不知道具体的类型。
使用泛型时,我们可以指定参数的具体类型。例如,我们可以创建一个只能存储整数的容器:
Container<Integer> container = new Container<>(42);
在这个例子中,我们使用了<Integer>
这个泛型类型参数,明确告诉编译器我们要存储的是整数类型的值。这样我们不仅可以获取容器中的值,也可以修改容器中的值。
总结来说,泛型是用来定义类或方法时指定类型的一种机制,而通配符是用来表示未知类型或接受任意类型参数的一种方式。泛型让我们可以更加安全和灵活地处理类型,而通配符让我们可以更加通用地处理类型。
网友评论