泛型编程的必要性
之所以需要泛型编程,是为了能让同样的功能处理不同类型的数据,就是为了突破类型限制复用代码功能。有一些方法,除了入参类型不一样外,其他的处理逻辑都是一样的,也就是说,他需要接收不同类型的参数。
1、关于泛型编程有一个核心需要记住:类型参数化,也即类型是一种可变的参数。
2、泛型有三种使用方式:泛型类、泛型接口、泛型方法。
3、类型参数有上界限定如T extends Number,但是没有类型下界限定用法T super Integer
4、通配符参数有上界限定 ? extends Number,修饰的形参是子类,子类往往只读。下界限定 ? super E ,修饰的形参是父类,父类往往可写。(子类赋值给父类)
5、当对参数只需要读时,可以使用类型参数和通配符,
6、当对参数需要写时,有继承关系时,可使用超类型通配符
7、当需要继承父类泛型方法时,可使用超类型通配符
// 泛型接口
public interface Map<K, V> {
}
class MapC implements Map<String,String>{
}
// 泛型类
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
// 泛型方法
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
泛型代码在编译时类型参数会被去掉,变成一个普通的类型,泛型参数有上下界的用上下界类型替换,没有的用Object替换。
限定类型上界
限定类型上界就是说给定的类型实参必须是限定类型或限定类型的子类型,通过extends关键字表示。
为什么需要使用类型上界?一、一定程度约束类型参数;二、限定之后反而扩大的接受参数的范围,本来函数addAll()只接受DynamicArray<Number>这种类型的,加了类型限定后,可以支持DynamicArray<T>,T是Number的子类就行,将类型反而扩大了,这个限定实际上扩大了范围。类型擦除以后,只会有一个特定的类型,但是加了类型限定以后,是一批子类类型都合适。
class Extend <U extends Number>{
}
// 为什么需要使用上界?
static class DynamicArray<E> {
public void add(E e){}
public void addAll(DynamicArray<E> c){}
}
public static void main(String[] args) {
// 将子类的元素复制到父类中,很合理的需求,但是编译不过,因为addAll()方法的入参要求是
// DynamicArray<Number> 而提供的却是DynamicArray<Integer>,类型不匹配,解决的办法就是
// 将addAll方法修改成:public <T extends E> void addAll(DynamicArray<T> c) {}
DynamicArray<Number> nums = new DynamicArray<>();
DynamicArray<Integer> ints = new DynamicArray<>();
ints.add(1);
ints.add(2);
nums.addAll(ints); // 这里报错
}
泛型通配符
上面addAll()方法可以用通配符的方式达到同样的效果:
public void addAll2(DynamicArray< ? extends E> c){}
那区别在哪里 T extends E和?extends E的区别是什么?
1、T extends E是定义类型参数
2、?extends E 是实例化已有的类型参数,但是实例的类型未知,只是限定了一个范围
泛型继承
1、子类可以继承父类的泛型参数,此时子类的泛型参数必须包含父类的泛型参数,并可以拥有自己独立的泛型参数
2、子类可以实例化父类的参数,此时子类的泛型参数列表里不用包含父类类型参数,如果有和父类类型参数同名的泛型参数,已经不代表去实例化父类的类型参数了,是子类独立的泛型参数了。
static class Pair<U, V> {
private U u;
private V v;
public Pair(U u, V v) {
this.u = u;
this.v = v;
}
public Pair() {
}
void f() {
System.out.println(u);
System.out.println(v);
}
}
// 1、子类可以继承父类的泛型参数,此时子类的泛型参数必须包含父类的泛型参数,并可以拥有自己独立的泛型参数
static class NumberPair<V extends Number, U extends Map, X> extends Pair<U, V> {
private V vv;
private U uu;
public NumberPair(U u, V v) {
super(u, v);
this.uu = u;
this.vv = v;
}
void ff() {
System.out.println(vv);
System.out.println(uu);
}
}
// 2、子类可以实例化父类的参数,此时子类的泛型参数列表里不用包含父类类型参数,
// 如果有和父类类型参数同名的泛型参数,已经不代表去实例化父类的类型参数了,是子类独立的泛型参数了。
class NumberPair2<C, D, U> extends Pair<String, Integer> {
}