11.7 泛型

我们在之前的容器的学习中,经常看到这样的形式List<string>,这里的String其实就是指定List的类型,List的这种可以接受未知类型的语法,就是泛型。
泛型可以抽象化代码,可以让代码更少更简洁。
泛型是指未指定具体的类型。通常使用T、E等字母代替,同时用一对尖括号围起来,表示这个是泛型。使用泛型就意味着这个类、接口或者方法适用于任何类。泛型类非常典型的应用就是容器类,容器类都使用了泛型,它们的元素可以是任何类型,在使用的时候指定就可以了。
1.泛型类
泛型类是指类的定义中使用了泛型,例如各种容器类ArrayList,下面是一个简单的泛型类:
public class Generic{
    private T key;
    public Generic(T t){
        key=t;
    }
    public void print(){
        System.out.println(key.toString());
    }
}
这个类要使用的话,需要指定具体的类型,例如这样:
Generic<String> t=new Generic("good");
t.print();
Generic<Integer> i=new Generic <>(5);
t.print();
Generic<Double> e=new Generic(5.6);
e.print();
这里使用了两个不同的类型,泛型的指定类型,必须是类,不能是简单类型(int,double,char,long这些),所以上面的只能用Integer而不能用int。
声明的时候指定了类型,在new后面可以不用再指定类型,上面的代码第1行和第3行都是符合语法的。
而第5行代码两边都没有指定,你需要确保参数能够隐式转换成所需要的类型,这里5.6可以转换成Double。最好不要写这样的代码。
Chapter c=new Chapter();
Generic<Chapter> i=new Generic <>(c);
i.print();
像这样写的话,Chapter类型就成为具体的类型注入了。

2.泛型接口
泛型还可以用在接口上。定义起来和类类似,例如下面是Set的定义,它继承的Collection同样是泛型接口
public interface Set<T> extends Collection<T>{
    ...省略;
}
例如框架SpringBoot的Jpa接口:
public interface JpaRepository <T,ID> extends PagingAndSortingRepository<T,ID> ,QueryByExampleExecutor<T>{
    ...省略;
}
这里出现了两个泛型T和ID,并没有限制泛型的数量。

3.带泛型的方法
方法的参数、返回类型都可以是泛型,通常都是在泛型类内部。也可以是静态方法。例如下面的getKey方法,返回的T就是泛型类的泛型T。
public class Generic<T>{
    private T key;
    public Generic(T t){
        key=t;
    }
    public void print(){
        System.out.println(key.toString());
    }
    public T getKey(){
        return key;
    }
}
如果要写一个方法,它的返回并不一定是类指定的那个泛型,那这样的方法就需要这么定义了:
public class Generic<T>{
    private T key;
    public Generic(T t){
        key=t;
    }
    public void print(){
        System.out.println(key.toString());
    }
    public T getKey(Genericmain){
        return(T)main.key;
    }
}
看起来有点奇怪的语法,下面是使用上面的getKey的例子:
public static void main(String[]args){
    Generic<String> t=new Generic("good");
    String g=t.getKey(t);
    System.out.println(g);
}
getKey方法的参数实际上是不需要的,可以在方法内部用ths。但为了说明方法也是一个泛型,所以故意这么写。