泛型

泛型

泛型的作用

  • 多种数据类型执行相同的代码
  • 规范数据类型,在编译时发出警告,防止出现cast异常(类型异常:Ineger和String类型比较就会发生cast异常)

泛型的使用

  • 泛型类
public class NormalGeneric<T>{
	private T data;
	
	public T getData(){
		return data;
	}
}

public static void main(String[] args) {
        NormalGeneric<String> normalGeneric = new NormalGeneric<>();
        normalGeneric.setData("12345");
        System.out.println(normalGeneric.getData());  // 输出12345
    }

声明了一个泛型类,泛型为T,T类似一个形参,在使用这个类的时候为这个T传入一个实参,类中所有用到的T都替换成实参String,实参必须是一个对象类型;

  • 泛型接口:
public interface People<T>{
        public T talk();
    }

定义了一个People接口,指定了一个泛型T,接口内部有一个talk抽象方法;

实现方式1:泛型类实现泛型接口

class Student<T> implements People<T>{
        @Override
        public T talk() {
            return null;
        }
    }

实现方式2:普通类实现泛型接口(直接指定泛型的实际类型)

class Teacher implements People<String>{
        @Override
        public String talk() {
            return null;
        }
    }
  • 泛型方法:
public  <T> T plus(){
        T result = null;
        //  todo
        return result;
    }

在使用这个方法的时候指定泛型的实际类型

normalGeneric.<Integer>plus(123);

也可以省去泛型的实际类型,编译器借助传入的参数会自动设置泛型的值

normalGeneric.plus("abc");

泛型变量的限定

public <T extends Comparable> T max(T a,T b){
        return a.compareTo(b)>0?a:b;
    }

class Man<T extends Comparable>{
        T man;
        public T getMan() {
            man.compareTo(man);  //  具有Compareable接口属性
            return man;
        }
    }

如果我们需要泛型变量具有特定接口的属性,可以给泛型T 继承一个接口,用来限定这个泛型的变量必须拥有这个接口的属性,传入的a和b必须实现Compareable接口;
T的extends后面,如果有类必须写在第一个,并且只能有一个类,可以有多个多实现

泛型的局限

  • 不能实例化泛型类型:不能new T()
  • 静态域不能引用类型变量:应为static静态属性先加载,而泛型的确认是在对象new的时候才知道;
  • 必须是包装类型,不能是基础类型
  • 不能使用instanceof判断对象类型

img

  • 泛型类不能继承异常

img

泛型的通配符

类型之间的继承关系不能保证泛型之间的继承关系
下面举一个例子说明这句话的意思

首先声明了如下四个继承关系的类

public class Food {
}

public class Fruit extends Food {
}

public class Apple extends Fruit {
}

public class Orange extends Fruit {
}

再声明一个标准的泛型类

public class GenericType<T> {
    private T data;
    public void setData(T data) {
        this.data = data;
    }
    public T getData() {
        return data;
    }
}

img

我们声明了一个eat方法,传入的参数是泛型为Fruit的标准泛型类,创建了两个标准泛型类,一个泛型是Fruit,一个泛型是Apple,当调用eat方法时,只有fruit有效,apple却不行,可是apple明明继承了fruit,也是水果,为什么不能吃呢??

因为类型的继承不能保证泛型变量之间的继承关系,为了解决吃apple的问题,引入了通配符 ?

img

我们将泛型的"实参"改成? extends Fruit,表示接收上界为Fruit的类型(Fruit和Fruit的子类)

下面再理解一句话:通配符用于安全的访问数据

img

为什么setdata()的时候不管传入fruit还是apple都是不行的,因为通配符? extends Fruit规定了可能是Fruit,可能是Apple,也可能是Orange,不确定传入的是哪个,所以set的时候都不可以,但是在取出的时候,有一点可以保证,那就是一定是Fruit,(因为Apple和Orange也是Fruit),所以通配符用于安全的访问数据。

通配符只能用在泛型方法上,不可以用在泛型类上

super Apple表示Apple 和 Apple 的父类:安全的写入数据

img

super通配符可以set当前类和其子类,get Object类
extends通配符不能set , 只能get当前类和其父类

img

super通配符可以set当前类和其子类,get Object类
extends通配符不能set , 只能get当前类和其父类

泛型的原理

类型擦除:(在编译期完成)

在jdk中的泛型是伪泛型,泛型类型在编译的时候会被擦除,jvm看到的只有原始类型ArrayList<String> 和 ArrayList<Object> 在jvm看来是一样的

转载

posted @ 2021-03-18 08:40  AronJudge  阅读(80)  评论(0编辑  收藏  举报