Java泛型

133 阅读5分钟
1.为什么需要泛型?
//int类型相加
 public int addInt(int x,int y){
        return x+y;
    }
    
 //如果是float相加
 public float addFloat(float x,float y){
        return x+y;
    }
 
 //如果是double呢?...相同一段代码,仅仅是因为传入的参数不同,就重写一遍方法,很是麻烦,这就需要泛型了。
    
2.泛型的好处:
  1. 适用于多种数据类型,执行相同的代码
  2. 规定了数据类型后,在编译期就会发现错误,避免了在使用时运行期的报错
 List list = new ArrayList();
        list.add("apple");
        list.add("100");
        list.add(100);
        //编译不会报错,因为没有给list规定类型,这会的list是object类型,往里放的时候不会报错,但是取数据的时候会报类型转换异常。
      
//如果规定list为String类型,那么放入int类型,就会报错
List<String> list = new ArrayList();
        list.add("apple");
        list.add("100");
        list.add(100);
3.泛型的定义

参数化的类型

//泛型类
public class NormalGeneric<T> {
    private T data;


    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }


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



//泛型接口
//第一种实现方式,泛型类实现泛型接口
public interface IGenertor<T> {
        public T getData();
}

public class ImplGenertor<T> implements IGenertor<T> {
    @Override
    public T getData() {
        return null;
    }
}
//第二种实现方式
public class ImplGenertor1 implements IGenertor<String>{
    @Override
    public String getData() {
        return null;
    }
}

//泛型方法
//泛型方法是独立的,不用非要在泛型类或者泛型接口里
public class GenertorMethod {

    public <T>T ggetGenertor(T...t){
        return t[t.length/2];
    }
}
public static void main(String[] args) {
        GenertorMethod genertorMethod = new GenertorMethod();
        System.out.println(genertorMethod.<String>getGenertor("张三","李四"));//使用时指定类型
        System.out.println(genertorMethod.getGenertor(1,2,3,4));//也可以不指定,高版本编译器可以自行判断
    }
    
public class NormalFenertor2 {
    public class Generic<T>{
        private T key;

        public Generic(T key) {
            this.key = key;
        }

        //这不是泛型方法,只是一个普通方法。
        public T getKey() {
            return key;
        }

        //这也不是泛型类,只是一个普通方法。只是是用了Generic<Number>这个泛型类做行参而已。
        public void show(Generic<Number> obj){

        }
    }
}



public class NormalGeneric3 {

    static class Fruit{

        @Override
        public String toString() {
            return "fruit";
        }
    }

    static class Apple extends Fruit{

        @Override
        public String toString() {
            return "apple";
        }
    }

    static class Person{

        @Override
        public String toString() {
            return "person";
        }
    }


    static class GenerateTest<T>{

        //普通方法 泛型类中的T只会影响普通方法中的T
        public void show(T t){
            System.out.println(t.toString());
        }

        //泛型方法,使用泛型E,这个泛型E可以为任意类型
        //可以与T相同,也可以不同
        //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,
        //编译器也能够正确识别泛型方法中识别的泛型
        public <E> void show_2(E e){
            System.out.println(e.toString());
        }

        //在泛型类型中声明了一个泛型方法,使用泛型T
        //这个泛型T是一个全新的类型,可以与泛型类中声明的T不是同一个类型
        public <T> void show_3(T t){
            System.out.println(t.toString());
        }

    }


    public static void main(String[] args) {
        Apple apple = new Apple();
        Person person = new Person();
        GenerateTest<Fruit> generateTest = new GenerateTest<>();
        generateTest.show(apple);
//        generateTest.show(person);//泛型已经规定了类型,再重新使用类型就报错


        generateTest.show_2(apple);
        generateTest.show_2(person);


        generateTest.show_3(apple);
        generateTest.show_3(person);
    }


}


4.限定类型变量
public class ArrayAlg {
    //要保证a和b有compareTo的方法,就要在泛型上限制,让T extends Compareable,
    // 可以继承多个类或接口,但是如果类和接口混用,类要写在继承的第一个,并且只能有一个类,
    //因为在java里是单继承,多实现,所以只能继承一个类
    public static <T extends Comparable> T min(T a,T b){
        if (a.compareTo(b) > 0) return a;else return b;
    }

    public static <T extends ArrayList & Comparable & Serializable> T min(T a, T b){
        if (a.compareTo(b) > 0) return a;else return b;
    }
}

5.泛型中的约束和局限性
1.不能实例化类型变量
2.静态域或方法里不能引用类型变量

因为 对象创建的时候才知道泛型的类型,对象创建的时候先执行静态方法,再执行构造方法。虚拟机不知道泛型是什么类型

3.静态方法本身是泛型方法就行
public class Restrict<T> {

    private T data;

    //正常的构造方法
    public Restrict(T data) {
        this.data = data;
    }

    public Restrict() {

    }

    //这样是错误的
//    public Restrict() {
//        this.data = new T();
//    }

    //静态域或方法里不能引用类型变量
    //因为 对象创建的时候才知道泛型的类型,对象创建的时候先执行静态方法,再执行构造方法。虚拟机不知道泛型是什么类型
//    public static T instance;//这个是错误的,不允许这样


    //静态方法本身是泛型就可以
    public static <T>T getInstance(){
        return null;
    }


    public static void main(String[] args) {
//        Restrict<double> //double是基础类型,所有的基础类型都不能当泛型类型,得要用包装类型Double,因为基础类型不是对象
        Restrict<Double> restrict = new Restrict<>();

        //泛型不能使用instanceof
//        if (restrict instanceof Restrict<Double>)
//        if (restrict instanceof Restrict<T>)

        Restrict<String> stringRestrict = new Restrict<>();
        //返回结果为true,不管什么类型都是返回一个类对象
        System.out.println(restrict.getClass() == stringRestrict.getClass());


        Restrict<Double>[] restricts;//可以定义泛型数组
        Restrict<Double>[] restrictArray = new Restrict[10];
       // Restrict<Double>[] restrictArray1 = new Restrict<Double>[10];//不能创建数组
    }

}

6.泛型的约束和局限性
public class ExceptionRestrict {

    //泛型类不能继承 Exception Throwable
//    private class Problem<T> extends Exception{}

    //不能捕获泛型类对象
//    public <T extends Throwable> void doWork(T t){
//        try {
//
//        }catch (T t){
//
//        }
//    }

    //可以这样写
    public <T extends Throwable> void doWork(T t) throws T{
        try {

        }catch (Throwable e){

        }
    }

}

如果A类继承自B类,那么泛型A和泛型B没有任何继承关系

public class Employee {
}

public class Worker extends Employee {
}

public class Pair<T> {
    
    public static void main(String[] args) {
        //Pair<Employee> employeePair 和 Pair<Worker> workerPair完全是两个独立的,没有任何继承关系
        Pair<Employee> employeePair = new Pair<>();
        Pair<Worker> workerPair = new Pair<>();

        Employee employee = new Employee();
//        Pair<Employee> employeePair2 = new  Pair<Worker>();//报错,没有继承关系

        Pair<Employee> pair = new ExtendsPair<>();//因为ExtendsPair 继承 Pair
    }

    //泛型类可以继承或者扩展其他泛型类,比如List和ArrayList
    private static class ExtendsPair<T> extends Pair<T>{

    }
}

7.通配符

? extends Fruit ,通配符只能使用在方法上,类不能使用 extends一般用于安全的访问数据、读取数据 super可以安全的写入数据

public class Employee extends Person{
}

public class GenericType<T> {
    private T data;


    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}


 public static void main(String[] args) {
        GenericType<Employee> employeeGenericType = new GenericType<>();
        printChild(employeeGenericType);
        GenericType<Worker> workerGenericType = new GenericType<>();
        printChild(workerGenericType);
//        print1(new GenericType<Person>());//这是错误的,Person是Employee的父类,超过了界限

        GenericType<? extends Employee> childGenericType = employeeGenericType;//这样是没问题的
        GenericType<? extends Employee> childGenericType1 = workerGenericType;//这样是没问题的


        Employee employee = new Employee();
        Worker worker = new Worker();
        //三个都报错
//        childGenericType.setData(new Person());
//        childGenericType.setData(employee);
//        childGenericType.setData(worker);


        //extends用于安全的读取数据
        //set是set的Employee的子类,但是取的时候编译器不知道是哪个子类,
        //所以只能返回Employee对象
        employee = childGenericType.getData();
        //set是set的Employee的子类,但是取的时候编译器不知道是哪个子类,
        // 所以不能返回Worker对象,报错,得强转
        worker = (Worker) childGenericType.getData();


        GenericType<Person> personGenericType = new GenericType<>();
        printSuper(employeeGenericType);
        printSuper(personGenericType);
//        printSuper(workerGenericType);//Worker是Employee的子类,低于Employee不行


//super用于安全的写入数据
        GenericType<? super Employee> parentGenericType = new GenericType<>();
        parentGenericType.setData(new Worker());
        parentGenericType.setData(new Employee());
//        parentGenericType.setData(new Person());//不能设置比它大的
        Object parentData = parentGenericType.getData();//返回父类object
    }

//extends Employee 的Employee代表传入参数的上限,不能超过
    public static void printChild(GenericType<? extends Employee> genericType){}


    //super Employee 的Employee代表传入参数的下限,不能低于
    public static void printSuper(GenericType<? super Employee> genericType){} public static void main(String[] args) {
        GenericType<Employee> employeeGenericType = new GenericType<>();
        printChild(employeeGenericType);
        GenericType<Worker> workerGenericType = new GenericType<>();
        printChild(workerGenericType);
//        print1(new GenericType<Person>());//这是错误的,Person是Employee的父类,超过了界限

        GenericType<? extends Employee> childGenericType = employeeGenericType;//这样是没问题的
        GenericType<? extends Employee> childGenericType1 = workerGenericType;//这样是没问题的


        Employee employee = new Employee();
        Worker worker = new Worker();
        //三个都报错
//        childGenericType.setData(new Person());
//        childGenericType.setData(employee);
//        childGenericType.setData(worker);


        //extends用于安全的读取数据
        employee = childGenericType.getData();//set是set的Employee的子类,但是取的时候编译器不知道是哪个子类,所以只能返回Employee对象
        worker = (Worker) childGenericType.getData();//set是set的Employee的子类,但是取的时候编译器不知道是哪个子类,所以不能返回Worker对象,报错,得强转


        GenericType<Person> personGenericType = new GenericType<>();
        printSuper(employeeGenericType);
        printSuper(personGenericType);
//        printSuper(workerGenericType);//Worker是Employee的子类,低于Employee不行


//super用于安全的写入数据
        GenericType<? super Employee> parentGenericType = new GenericType<>();
        parentGenericType.setData(new Worker());
        parentGenericType.setData(new Employee());
//        parentGenericType.setData(new Person());//不能设置比它大的
        Object parentData = parentGenericType.getData();//返回父类object
    }

//extends Employee 的Employee代表传入参数的上限,不能超过
    public static void printChild(GenericType<? extends Employee> genericType){}


    //super Employee 的Employee代表传入参数的下限,不能低于
    public static void printSuper(GenericType<? super Employee> genericType){}

在java虚拟机是如何实现泛型的 ?

类型擦除、泛型擦除、强制转型