准备工作 🦘
依旧是DynamicArray的例子,继续上一篇博客🍄
package com.xmonster.demo2;
import java.util.Arrays;
public class DynamicArray<E> {
private static final int DEFAULT_CAPACITY = 10;
private int size = 0;
private Object[] elementData;
public DynamicArray(){
this.elementData = new Object[DEFAULT_CAPACITY];
}
private void ensureCapacity(int minCapacity){
int oldCapacity = elementData.length;
if(oldCapacity >= minCapacity){
return;
}
int newCapacity = oldCapacity * 2;
if(newCapacity < minCapacity){
newCapacity =minCapacity;
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
public void add(E e){
ensureCapacity(size+1);
elementData[size++]=e;
}
public E get(int index){
return (E)elementData[index];
}
public int size(){
return size;
}
public E set(int index, E element){
E oldValue = get(index);
elementData[index] = element;
return oldValue;
}
// public <T extends E> void addAll(DynamicArray<T> c){
// for (int i = 0; i < c.size; i++) {
// add(c.get(i));
// }
// }
public void addAll(DynamicArray<? extends E> c){
for (int i = 0; i < c.size; i++) {
add(c.get(i));
}
}
public static <T extends Comparable<T>> T max(T[] arr){
T max=arr[0];
for (int i = 0; i < arr.length; i++) {
if(arr[i].compareTo(max)>0){
max=arr[i];
}
}
return max;
}
}
玩x1 🐒
按常理来说,通配符能够使用的场合,就一定可以转换成类型参数的形式。
上篇博客的末尾内容:
public int indexOf(DynamicArray<? > arr, Object e)
效果等同于:
public <T> int indexOf(DynamicArray<T> arr, Object e)
但是,别忘了,使用通配符只能读,不能写
这里又有一种情况,那就是最简单的数组中两者交换位置的操作,这个操作可以理解成:
public void swap(DynamicArray<? > arr, int i, int j){
Object tmp = arr.get(i);
arr.set(i, arr.get(j));
arr.set(j, tmp);
}
这段代码按常理来说确实没什么问题,但是事实是:
编译器直接报错了!错误原因:
它很明显,它想让我转换类型,如果使用通配符?来表示的话显然不行了,因为这两种set()方法是非法的
,但是同样的内部主体代码,如果说我改成了借助类型参数的泛型方法又会怎么样呢?
public <T> void swapInternal(DynamicArray<T> arr, int i, int j){
T tmp = arr.get(i);
arr.set(i, arr.get(j));
arr.set(j, tmp);
}
public void swap(DynamicArray<? > arr, int i, int j){
swapInternal(arr, i, j);
}
编译器并没有报错!测试一下
可以看到顺序成功的调换了!
这种情况称为“需要写的场合”,这种场合显然是不能够使用通配符的,因为使用通配符的地方只能“读”,你现在的做法显然是要“移动”元素,不是简单的读,那么必然报错
玩x2 🐈
如果参数类型之间存在“依赖关系”,常用的就是extends,那么这种时候也只能够使用类型参数
,比如我现在想把src容器中的内容搞到dest容器(注意此时src容器的类型变量必须是dest容器的子类,这里明显有一个继承关系)
这里我觉得很好理解,如果使用通配符,如果两个都是使用通配符,那么肯定存在安全隐患,肯定报错,这里显示正确版本,即使用类型参数的泛型方法
public static <D,S extends D> void copy(DynamicArray<D> dest, DynamicArray<S> src){
for (int i = 0; i < src.size(); i++) {
dest.add(src.get(i));
}
}
这里也可以这样玩儿:
public static <D> void copy(DynamicArray<D> dest, DynamicArray<? extends D> src){
for (int i = 0; i < src.size(); i++) {
dest.add(src.get(i));
}
}
可以理解成“简约”版本
玩x3 🐕
还有一种情况:那就是返回值依赖于类型参数时,肯定也是不能使用通配符,这里没什么好说了,可以参考上面DynamicArray的最后一个方法,是一个求最大值的方法,那个方法就是典型例子。
这里提供Comparable接口:
package com.xmonster.demo2;
public interface Comparable<T> {
public int compareTo(T o);
}
总结 🐾
最后,那就是我们在使用的时候,很经常将这两者一起结合使用,就像上面的copy方法一样,定义必要的类型参数,使用通配符表达依赖,并接受更广泛的数据类型。