?表示不确定的 java 类型
T (type) 表示具体的一个java类型
K V (key value) 分别代表java键值中的Key Value
E (element) 代表Element

一个简单的泛型类

GenericsTest类引入了一个类型变量T,它指定了方法的返回类型以及域和局部变量的类型
可以看到不指定类型时需要强转、指定类型时就不需要了

public class GenericsTest<T> {

    private T t;
    public void set(T t){
        this.t=t;
    }
    public T get(){
        return this.t;
    }


    @Test
    void test(){
        GenericsTest number=new GenericsTest();
        number.set(13);
        //不指定类型时需要强转
        Integer num =(Integer) number.get();
        System.out.println(num);
        GenericsTest<String>name=new GenericsTest<>();
        name.set("liuyuan");
		//指定类型就不需要强转了
        String getName = name.get();
        System.out.println(getName);
    }

}

泛型方法

泛型方法可以在普通类中定义也可以在泛型类中定义,调用泛型方法时可以在方法名前的尖括号中放入具体的类型

public class GenericsTest<T> {

    private T t;
    public void set(T t){
        this.t=t;
    }
    public T get(){
        return this.t;
    }
    /**
     *  类型变量放在修饰符public static后面,返回类型的前面
     * @param a
     * @param <T>
     * @return
     */
    public static <T> T getMiddle(T ...a){
        return a[a.length/2];
    }

    @Test
    void testGetMiddle(){
        //大多情况可以直接省略<String>不写,编译器能够推断出类型
        String middle = GenericsTest.getMiddle("left", "middle","middle2", "right");
        System.out.println(middle);
        // 可以指定类型
        Integer middle1 = GenericsTest.<Integer>getMiddle(123, 22, 56);
        System.out.println(middle1);
    }

}

类型变量的限定

场景:对数组排序想要最小值,但是必须下面的smallest实现了Comparable接口我们才能使用其compareTo方法对其进行判断,此时通过对类型变量T设置限定实现
<T extends Comparable> 此时确定了T所属的类有了compareTo方法就能实现我们想要的结果

public static <T extends Comparable> T getMinNum(T arr[]){
        if(arr == null || arr.length== 0 ) return null;
        T smallest=arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(smallest.compareTo(arr[i])>0){
                smallest=arr[i];
            }
        }
        return smallest;
    }

    @Test
    void getMinNumTest(){
        String arr []=new String[]{"b","a","d"};
        String minNum = getMinNum(arr);
        System.out.println(minNum);
        Integer arr1 []=new Integer[]{6,4,1};
        System.out.println(getMinNum(arr1));
    }

提一嘴:这里的关键词为什么用extends去限定,T应该是绑定的类型的子类,而T和绑定类型可以是接口或类,选择extends更接近子类概念 所以不用implements
另外:一个类型变量或通配符可以有多个限定
T extends Comparable & Seralizable,限定类型用&分割,而逗号用来分割类型变量

最大值和最小值案例

1、<T extends Comparable>限定类型变量
2、参数返回Pair

public static <T extends Comparable> Pair<T> getMAxAndMin(T arr[]){

        if(arr == null || arr.length == 0)return null;
        T min=arr[0];
        T max=arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(min.compareTo(arr[i])>0){
                min=arr[i];
            }
            if(max.compareTo(arr[i])<0){
                max=arr[i];
            }
        }
        return new Pair(min,max);
    }
@AllArgsConstructor
public  class Pair<T>{
    @Getter
    @Setter
    T first;
    @Getter
    @Setter
    T second;
}

泛型类型的继承规则

使用泛型类型时,比如List<Animal.Dog>List<Animal>的子类吗???肯定不是

通配符类型

通配符解决什么问题???
比如想要打印动物的信息:

public void printAnimal2(List<Animal> animals){
    for (int i = 0; i < animals.size(); i++) {
        System.out.println(animals.get(i));
 }

但是你不能把List<Animal.Dog>也就是狗的集合传进去,这点是受限制的,解决办法就是使用通配符类型。
1、public void printAnimal3(List<? extends Animal>animals) 类型List<Animal.Dog>List<Animal>的子类型
2、换种姿势实现上述需求
public <T extends Animal> void printAnimal(List<T> animals) 指定了List<T> animals只能够是Animal的子类
两种方式实现代码如下:

@AllArgsConstructor
@Data
public class Animal {

    private String name;


    private String sex;


    private Integer age;

   public static class Dog extends Animal{

        public Dog(String name, String sex, Integer age) {
            super(name, sex, age);
        }
    }

    public static class Cat extends Animal{
        public Cat(String name, String sex, Integer age) {
            super(name, sex, age);
        }
    }

}

想把List<Animal.Dog>打印出来报错

@Test
    void testPrintAnimal(){
        List<Animal.Dog> dogs=new ArrayList<>();
        dogs.add(new Animal.Dog("dog","1",18));
        // error
        printAnimal2(dogs);

    }
        public void printAnimal2(List<Animal> animals){
        for (int i = 0; i < animals.size(); i++) {
            System.out.println(animals.get(i));
        }
    }

而这样就可以两种方式都可以实现

//<T extends Animal> 对T进行了限制 只能是Animal子类 
  public <T extends Animal> void printAnimal(List<T> animals){
        for (int i = 0; i < animals.size(); i++) {
            System.out.println(animals.get(i));
        }
    }
    //? extends Animal 标示任何泛型List类型,类型参数是Animal子类即可
    public void printAnimal3(List<? extends Animal>animals){
        for (int i = 0; i < animals.size(); i++) {
            System.out.println(animals.get(i));
        }
    }
        @Test
    void testPrintAnimal(){
        List<Animal.Dog> dogs=new ArrayList<>();
        dogs.add(new Animal.Dog("dog","1",18));
        printAnimal(dogs);
        printAnimal3(dogs);

    }