Java泛型支持通配符,可以单独使用 '?' 来表示任意类型,也可以使用extends关键字表示某一个类或接口的子类,也可以使用super关键字表示某一个类,接口的父类型。
本文对读写操作的分析得出什么时候该使用extends 和 super。
<? super T>使“读”受到限制
先来看一个例子:
比如要实例化一个List<? super Integer>的numberList ,我们可以使用Integer, Number和Object来完成,如:
- List<? super Integer> numberList = new ArrayList<Integer>();
- List<? super Integer> numberList = new ArrayList<Number>();
- List<? super Integer> numberList = new ArrayList<Object>();
List<? super Integer> numberList = new ArrayList<Integer>(); List<? super Integer> numberList = new ArrayList<Number>(); List<? super Integer> numberList = new ArrayList<Object>();
从这个例子中可以看出,numberList可能是指向List<Integer>, 可能指向List<Number>, 也可能指向List<Object>, 这样多可能性将会限制“读”操作。
因为,
- 我们并不能保证读到的是Integer,因为numberList可能指向List<Number>或者List<Object>。
- 我们并不能保证读到的是Number,因为numberList可能指向List<Object>。
- 唯一能保证的的就是我们将得到一个Object或者是Object的子类的一个实例,但是我们并不知道具体的子类是什么。
如下,有如下这样的一个get方法,使用了List<? super T>:
- privatestatic <T> T get(List<? super T> list, int index) {
- }
private static <T> T get(List<? super T> list, int index) { }
那么,我们怎么知道list中存放的内容是什么类型呢? 我们只能知道是T或者T的父类,仅此而已。但T的父类具体是什么,不得而知了。
“读”操作不能使用<? super T>, 而应该使用<? extends T>, 让我们来看看Collections中的一些方法吧。
- /**
- * Gets the ith element from the given list by repositioning the specified
- * list listIterator.
- */
- privatestatic <T> T get(ListIterator<? extends T> i, int index) {
- T obj = null;
- int pos = i.nextIndex();
- if (pos <= index) {
- do {
- obj = i.next();
- } while (pos++ < index);
- } else {
- do {
- obj = i.previous();
- } while (--pos > index);
- }
- return obj;
- }
/** * Gets the ith element from the given list by repositioning the specified * list listIterator. */ private static <T> T get(ListIterator<? extends T> i, int index) { T obj = null; int pos = i.nextIndex(); if (pos <= index) { do { obj = i.next(); } while (pos++ < index); } else { do { obj = i.previous(); } while (--pos > index); } return obj; }
- publicstatic <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) {
- if (c==null)
- return binarySearch((List) list, key);
- if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
- return Collections.indexedBinarySearch(list, key, c);
- else
- return Collections.iteratorBinarySearch(list, key, c);
- }
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) { if (c==null) return binarySearch((List) list, key); if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD) return Collections.indexedBinarySearch(list, key, c); else return Collections.iteratorBinarySearch(list, key, c); }
<? extedns T>使“写”受到限制
一个List<? extends Number>的numberList可能指向List<Number>, 可能指向List<Integer>, 也可能指向List<Object>.
- List<? extends Number> numberList = new ArrayList<Number>();
- List<? extends Number> numberList = new ArrayList<Integer>();
- List<? extends Number> numberList = new ArrayList<Double>();
List<? extends Number> numberList = new ArrayList<Number>(); List<? extends Number> numberList = new ArrayList<Integer>(); List<? extends Number> numberList = new ArrayList<Double>();
这种多可能性将让<? extends T> 的“写”操作受到限制。
因为,
- 我们不能添加一个Integer类型的值,因为numberList可能指向List<Double>
- 我们不能添加一个Double类型的值,因为numberList可能指向的是List<Integer>
- 我们不能添加一个Number类型的值,因为numberList可能指向的是List<Integer>
我们不能添加任何对象到List<? extends T>, 那是因为我们并不能保证实际指向的是什么类型的List,所以也就不能保证想要添加的对象是List所允许的类型。
唯一能保证的是只能读取并得到一个T或者是T的子类。
上面的分析,我们可以得出一个结论, 那就是<? extends T> 不适合“写”操作,<? super T> 不适合“读”操作。
其实,
Collections中的copy方法很好的使用<? extends T> 和 <? super T>的经典案例。
另外还有一个PECS原则供参考:
PECS原则-->
在 Collections#copy方法中,src (the producing list)使用extends, 而 desc (the consuming list) 使用super. 代码如下:
- publicstatic <T> void copy(List<? super T> dest, List<? extends T> src) {
- int srcSize = src.size();
- if (srcSize > dest.size())
- thrownew IndexOutOfBoundsException("Source does not fit in dest");
- if (srcSize < COPY_THRESHOLD ||
- (src instanceof RandomAccess && dest instanceof RandomAccess)) {
- for (int i=0; i<srcSize; i++)
- dest.set(i, src.get(i));
- } else {
- ListIterator<? super T> di=dest.listIterator();
- ListIterator<? extends T> si=src.listIterator();
- for (int i=0; i<srcSize; i++) {
- di.next();
- di.set(si.next());
- }
- }
- }
public static <T> void copy(List<? super T> dest, List<? extends T> src) { int srcSize = src.size(); if (srcSize > dest.size()) throw new IndexOutOfBoundsException("Source does not fit in dest"); if (srcSize < COPY_THRESHOLD || (src instanceof RandomAccess && dest instanceof RandomAccess)) { for (int i=0; i<srcSize; i++) dest.set(i, src.get(i)); } else { ListIterator<? super T> di=dest.listIterator(); ListIterator<? extends T> si=src.listIterator(); for (int i=0; i<srcSize; i++) { di.next(); di.set(si.next()); } } }
通配符<?>的使用
List<?>表示的是任意类型。因为编译器不知道List中容纳的是什么类型的元素,所以不能对其进行增加,修改的操作。 但是,List<?>拥有删除的功能,因为这些功能与泛型类型没有关系。
所以,List<?>适合用于与泛型类型无关的方法,比如remove, shuffle等。
我们来看看Collections中的几个方法吧:
- publicstaticvoid shuffle(List<?> list, Random rnd) {
- int size = list.size();
- if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
- for (int i=size; i>1; i--)
- swap(list, i-1, rnd.nextInt(i));
- } else {
- Object arr[] = list.toArray();
- // Shuffle array
- for (int i=size; i>1; i--)
- swap(arr, i-1, rnd.nextInt(i));
- // Dump array back into list
- ListIterator it = list.listIterator();
- for (int i=0; i<arr.length; i++) {
- it.next();
- it.set(arr[i]);
- }
- }
- }
public static void shuffle(List<?> list, Random rnd) { int size = list.size(); if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) { for (int i=size; i>1; i--) swap(list, i-1, rnd.nextInt(i)); } else { Object arr[] = list.toArray(); // Shuffle array for (int i=size; i>1; i--) swap(arr, i-1, rnd.nextInt(i)); // Dump array back into list ListIterator it = list.listIterator(); for (int i=0; i<arr.length; i++) { it.next(); it.set(arr[i]); } } }
- publicstaticvoid swap(List<?> list, int i, int j) {
- final List l = list;
- l.set(i, l.set(j, l.get(i)));
- }
public static void swap(List<?> list, int i, int j) { final List l = list; l.set(i, l.set(j, l.get(i))); }
- publicstaticvoid rotate(List<?> list, int distance) {
- if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD)
- rotate1(list, distance);
- else
- rotate2(list, distance);
- }
public static void rotate(List<?> list, int distance) { if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD) rotate1(list, distance); else rotate2(list, distance); }
总结:
- 只用于“读”功能时,泛型结构使用<? extends T>
相关推荐
Java泛型通配符T、E、K、V区别详解 ...Java泛型通配符T、E、K、V等都是Java泛型的通配符,它们的区别在于它们的名称和使用场景。在使用泛型时,我们可以根据需要选择合适的通配符,以提高代码的可读性和安全性。
Java泛型是Java编程语言中的一个强大特性,它允许我们在定义类、接口和方法时指定类型参数,从而实现代码的重用和类型安全。在Java泛型应用实例中,我们可以看到泛型如何帮助我们提高代码的灵活性和效率,减少运行时...
- **早期版本的局限**:在Java泛型出现之前,程序员通常使用Object作为容器类(如ArrayList、HashMap等)的默认类型,这导致了大量的类型转换操作,同时也容易引发ClassCastException。 - **类型擦除**:Java泛型...
Java泛型中T和问号(通配符)的...Java泛型中T和问号(通配符)的区别在于它们的使用场景和含义。T是泛型类型参数的代表,而问号(通配符)是泛型类型参数的占位符。正确地使用T和问号(通配符)可以提高代码的重用率和安全性。
Java泛型是Java编程语言中的一个关键特性,它在2004年随着Java SE 5.0的发布而引入,极大地增强了代码的类型安全性和重用...Java泛型技术的不断发展和完善,也为Java程序员提供了强大的工具来应对复杂的数据处理场景。
本文将深入探讨Java泛型的工作原理、应用场景以及最佳实践。 Java泛型是提高代码重用性、类型安全性和减少类型转换错误的重要工具。了解泛型的原理、声明方式、类型擦除、边界以及通配符的使用,对于掌握Java编程中...
Java泛型是Java编程语言中的一个...在“java泛型的高级用法demo”中,你可以期待更深入的示例,如限制类型参数、创建多级泛型、使用泛型接口等复杂应用场景。实践这些示例将有助于进一步理解并熟练掌握Java泛型的精髓。
Java 泛型总结(三):通配符的使用 通配符是 Java 泛型中一个重要的概念,它允许我们在泛型中实现向上转型,解决了泛型不支持协变的问题。本文将详细介绍通配符的使用和原理。 在 Java 中,数组是协变的,这意味...
本文将深入探讨Java泛型通配符如何解决这一问题。 首先,我们需要理解泛型的基本概念。泛型允许我们在定义集合(如List)时指定元素的类型,确保在编译时期就能捕获类型错误。例如,`List<String>` 只能存储字符串...
感谢所有为Java泛型做出贡献的人们,包括设计者、实现者以及提供反馈和支持的社区成员。泛型是Java语言的一个重要特性,极大地提高了代码的质量和可维护性。 以上就是基于给定文件信息对Java 1.5泛型指南的主要知识...
除了指定具体的类型,Java泛型还支持类型通配符,如`<?>`,表示未知的类型。在某些场景下,当不需要访问具体类型信息,仅关心对象是否属于特定类型或其子类型时,类型通配符非常有用。 6. **边界限定** Java泛型...
学习Java泛型能够帮助我们更好地编写和使用通用的类、接口和方法。以下是从给定文件的标题、描述、标签和部分内容中提取出的详细知识点。 1. 泛型类和泛型方法: 在Java中,泛型可以应用于类、接口和方法。泛型类和...
"JAVA泛型笔记.pdf" Java 泛型(Generic)是 Java 5 中引入的一种机制,它允许开发者在编写类、接口和方法时指定类型参数,以提高代码的灵活性和可重用性。泛型的主要特点是可以在编译期检查类型的正确性,避免了...
Java泛型的一个重要应用是在集合框架中。在JDK 1.5之前,Java集合类框架使用Object作为元素类型,这意味着集合可以存储任何类型的对象。然而,这种设计导致在从集合中获取元素时必须进行类型转换,这个过程是繁琐且...
通过阅读"Java Generics and Collections",开发者不仅可以掌握Java泛型和集合的基本使用,还能深入了解它们的高级特性和最佳实践,从而在实际项目中编写出更高质量的代码。这本书对于Java程序员来说是一份宝贵的...
在Java中,我们可以在集合类如List、Set、Map等中使用泛型,以确保存储的数据类型一致。例如,创建一个只存储String类型的ArrayList: ```java List<String> list = new ArrayList(); ``` 2. 通配符: 通配符...
Java泛型是Java编程语言中的一个特性,它允许在类、接口和方法中使用类型参数,从而提高了代码的重用性和安全性。在这个“java泛型容器堆栈代码”中,我们将会探讨Java如何利用泛型来创建高效且类型安全的容器,特别...