泛型

111 阅读3分钟

由泛型数组列表引入

ArrayList是一个有类型参数(type parameter)的泛型类(generic class),可自适应大小,取代Vector类。
但也存在缺点:增加了访问元素语法的复杂度,需要使用set()get()
Collections可实现集合的泛型算法。

//声明
ArrayList<Employee> staff = new ArrayList<Employee>();
ArrayList<Employee> staff = new ArrayList<>(100);
ArrayList<Employee> staff = new ArrayList<>();
//Java10中最好使用var关键字避免重复写类名
var staff = new ArrayList<Employee>();
//分配大小
staff.ensureCapacity(100);
//添加元素
staff.add(new Employee("Harry Hacker",...));
//返回元素个数
staff.size();
//设置第1个元素为harry
staff.set(2,harry);
staff.get(2);
staff.remove(2);
staff1.addAll(staff);


Collections.sort();

泛型程序设计

泛型类产生的一个原因就是为了创造容器类。由ArrayList可见,该代码可以对多种不同类型的对象重用,容器类算得上最具重用性的类库之一了。泛型的出现也可将一组对象直接打包存储于单一对象一并返回,即一次方法调用就返回多个对象。

定义泛型类

public class Pai<T>
{
    private T first;
    private T second;
    
    public Pair() { first = null; second = null; }
    public Pair(T first, T second) { this.first = first; this.second = second; }
    
    public T getFirst() { return first; }
    public T getSecond() { return Second; }
    
    public void setFirst(T newValue) {first = newValue; }
}

//实例化
Pair<String>();

泛型接口

工厂方法设计模式的一种应用。

定义泛型方法

class ArrayAlg{
    public static <T> T getMiddle(T...a){
        return a[a.length / 2];
    }
    
    public static <T extends Comparable> Pair<T> minmax(T[] a){
        ...
        return new Pair<>(min,max);
    }
    
    //Java可implements多个接口,但只能extends一个类
    <T extends Comparable & Serializable>
}

String middle = ArrayAlg.<String>getMiddle("John","Q.","Public");
String middle = ArrayAlg.getMiddle("John","Q.","Public");

泛型代码与虚拟机

虚拟机中没有泛型类对象,都是普通类对象,因为编译器会擦除类型参数,类型参数会替换为它们的限定类型,如用Object替换<T>,用合成桥方法保持多态。为保持类型安全性,必要时在结果字节码中插入强制类型转换。而C++则会为每个模板实例化产生不同的类型,形成“模板代码膨胀”。

public static <T extends Comparable>T min(T[] a)
//擦除类型后只剩一个方法
public static Comparable min(Comparable[] a)
//桥方法
public void setSecond(Object second) { setSecond((LocalDate) second);}

限制与局限性

1.不能使用基本类型实例化类型参数
2.运行时类型查询只适用于原始类型:getClass()总是返回原始类型。

Pair<String> sringPair = ...;
Pair<Employee> employeePair = ...;
if(stringPair.getClass() == employeePair.getClass())
//true,都是Pair.class,并不比较String和Employee

3.不能创建参数化类型的数组,应直接使用ArrayList

var table = new Pair<String>[10];//ERROR
//擦除后
Object[] objarray = table;
objarray[0] = "Hello";//ERROR
//不过对于泛型类型,擦除会使以下机制无效
objarray[0] = new Pair<Employee>();
//可声明但不可初始化,会产生ClassCastException异常

4.Varargs警告
S:向参数个数可变的方法传递一个泛型类型的实例。

Collection<Pair<String>> table = ...;
Pair<String> pair1 = ...;
Pair<String> pair2 = ...;
addAll(table,pair1,pair2);

JVM会建立Pair<String>数组,会发出警告,不抛出错误。可用@SuppressWarnings("unchecked")@SafeVarargs直接注解addAll()
@SafeVarargs只能用于声明为staticfinal或Java9中private的构造器和方法
5.不能实例化类型变量(不可new T(),会擦除为Object)
6.泛型类的静态上下文中类型变量无效。
7.不能抛出或捕获泛型类的实例。
8.子类父类的<T>Pair两个实例无关系。

通配符

<? super/extends Manager>