Java一泛型

368 阅读5分钟

1、为什么需要泛型?

举个例子:

public class Genericity{
    public int sumInt(int a,int b){
        return a + b;
    }
    
    public double sumDouble(double a,double b){
        retrun a + b;
    }
    
    public <T> T sum(T a, T b){
        retrun a + b;
    }
}

上面代码中可以看出,sum()方法就可以替代sumInt()和sumDouble(),这就是我们为什么需要泛型。因为泛型用起来安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。说直白的就是让我写更少的代码做更多事情(个人肤浅的理解)

2、泛型类的定义

泛型(generic)是指参数化类型的能力

//泛型类
public class GenericityClass<T,K>{ //泛型设置可以是多个
    T data;
    K data2;
    public GenericityClass(){
        
    }
    public GenericityClass(T data){
        this.data = data;
    }
    public void setData(T data){
        this.data = data;
    }
    public T getData(){
        return data;
    }
    
    public static void main(String[] args){
        GenericityClass<String,String> genericityClass = new GenericityClass();
        genericityClass.setData("Hello World");
    }
  
}
//泛型接口以及泛型接口的实现
public interface GenericityInterface<T>{
    T getData();
}   
//泛型接口实现一
public class ImplGenericityInterface implements GenericityInterface<String>{
    
    public String getData(){
        return null;
    }
}

//泛型接口实现二
public class ImplGenericityInterface2<T> implements GenericityInterface<T>{
    
    public T getData(){
        return null;
    }
}

//泛型方法以及泛型方法的辨析
public class GenericityMethod{
    
    //泛型方法 最重要的标志 <T> T可以用其他字母代替
    public <T> void printMsg(T msg){
        System.out.printLn(msg.toString());
    }
    //泛型方法
    public <T> T returnMsg(T data){
        return data;
    }
    
}


public class GenericityClass2<T>{
    T data;
    
    //此方法不是泛型方法,是普通方法
    public T getData(){
        return data;
    }
    //泛型类中的泛型方法
    public <E> showMsg(E msg){
        System.out.printLn(msg.toString());
    }
}

3、限定类型变量

public class GenericityLimit{
    //不知道 a,b的类型,所以就不知道a,b是否有compareTo()方法,故会出现编译错误
    //public static <T> T max(T a,T b){
    //    if(a.compareTo(b)>0)
    //        return a;
    //    else
    //        return b;
    //}
    
    // T extends Comparable 及限定类型变量,之后实现 Comparable接口的类才能使用此方法
    //extends之后可以是类也可以是接口
    // 如果有类也有接口,类必须写在前面,类有且只有一个,
    public static <T extends Comparable> T min(T a,T b){
        if(a.compareTo(b)> 0)
            return a;
        else
            return b;
    }
    //实现Comparable
    class Test implments Comparable{
          @Override
        public int compareTo(Object o) {
            return 0;
        }
    }
    
    public static void main(String[] args){
        GenericityLimit.min(new Test(),new Test());//现在就不会编译错误
    }
}

4、泛型中的约束和局限性

    public class GenericityRestrain<T>{
        T data;
        //错误 泛型要在对象实例化之后才能知道类型,所以静态域或者方法里不能引用类型变量
        //private static T instance;   
        
        //正确,静态方法 本身是泛型方法就行
        public static <T> void getInstance(){}
        
        
        /*泛型类不能extends Exception/Throwable*/
    //private class Problem<T> extends Exception;

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


    public <T extends Throwable> void doWorkSuccess(T x) throws T{
        try{

        }catch(Throwable e){
            throw x;
        }
    }
        
        public static void main(String[] args){
            //GenericityRestrain<double> genericity1 = new GenericityRestrain<>(); //泛型必须是对象,double是基本类型,应该是包装类Double 
            GenericityRestrain<Double> genericity2 = new GenericityRestrain<>();
            
            GenericityRestrain<String> genericity3 = new GenericityRestrain<>();
            
            //打印结果为 true 说明不管泛型传入的类型是什么,泛型的原生类型永远是不变的
            System.out.println(genericity2.getClass()==genericity3.getClass());
            //且打印的两个类名也是相同的
            System.out.println(genericity2.getClass().getName());
            System.out.println(genericity3.getClass().getName());
            
        }
    }

5、通配符类型

    //动物类
    public class Animal{} 
    //狗类
    public class Dog extends Animal{}
    //猫类
    public class Cat extends Animal{}
    //阿拉斯加 狗的子类
    public class DogAla extends Dog{}
    //标准的泛型类
    public class GenericityType<T>{
        private T data;
        public void setData(T data){
            this.data = data;
        }
        public T getData(){
            return data;
        }
    }
    
    //测试类
    public class Test{
    
        public static void print1(GenericityType<Animal> g) {
            System.out.println(g.getData());
        }
        
        public static void use() {
            GenericityType<Animal> genericityAnimal = new GenericityType<>();
            print1(genericityAnimal);
            GenericityType<Dog> genericityDog = new GenericityType<>();
            //print1(genericityDog); //类型错误
        }

        public static void print2(GenericityType<? extends Animal> g) {
            System.out.println(g.getData());
        }

        public void useExtends() {
            GenericityType<Animal> genericityAnimal = new GenericityType<>();
            print2(genericityAnimal);
            GenericityType<Dog> genericityDog = new GenericityType<>();
            print2(genericityDog);
    
            //? extends  上界通配符 及能达到最高的父类   目的就是安全的访问数据
            GenericityType<? extends Animal> genericityType = new GenericityType<>();
            Animal animal = new Animal();
            Dog dog = new Dog();
            Cat cat = new Cat();
            //因为编译器只知道传入的是Animal 的子类,但是具体是哪一个子类,编译器是不知道的,所以报错
    //        genericityType.setData(animal); // 编译错误,不能存
    //        genericityType.setData(dog);
    //        genericityType.setData(cat);
    //        Dog dog1 = genericityType.getData(); //编译错误
    //        Cat cat1 = genericityType.getData();//编译错误
            //因为传入的data肯定都是Animal或者其子类,但是取的时候因为不知道其具体类型,则只能返回父类类型
            Animal animal1 = genericityType.getData();
        }
    
        public static void print3(GenericityType<? super Dog> g) {
            System.out.println(g.getData());
        }
    
        public static void useSuper() {
            GenericityType<Animal> genericityAnimal = new GenericityType<>();
            GenericityType<Dog> genericityDog = new GenericityType<>();
            GenericityType<DogAla> genericityDogAla = new GenericityType<>();
            GenericityType<Cat> genericityCat = new GenericityType<>();
    
            print3(genericityAnimal);
            print3(genericityDog);
    //        print3(genericityCat); //编译错误 Cat 不是Dog的子类或者父类
    //        print3(genericityDogAla); //编译错误 DogAla 是 Dog 派生出的子类 ,则下界通配符不能使用
    
            // ? super 下界通配符 及能达到最低的子类   主要用于安全的写入数据
            GenericityType<? super Dog> genericityType = new GenericityType<>();
            Animal animal = new Animal();
            Dog dog = new Dog();
            DogAla dogAla = new DogAla();
    //        genericityType.setData(animal); //编译错误
            genericityType.setData(dog);
            genericityType.setData(dogAla);
    
            //只能返回Object ,这里是一定是返回Animal 的超类,因为没有限定,所以不知道是哪一个超类,及返回Object
            Object data = genericityType.getData();
        }

    }

6、虚拟机是如何实现泛型的?

Java中的泛型,是伪泛型 ,jdk 做了泛型擦除处理,举例说明如下

//例子1
public class Genericity<T>{
    private T data;
    public void setData(T data){
        this.data = data;
    }
    public T getData(){
        return this.data;
    }
}

//jdk 编译之后,把T改成Object 如下
public class Genericity<Object>{
    private Object data;
    public void setData(Object){
        this.data = data;
    }
    public Object getData(){
        return this.data;
    }
}

//例子2
public class Genericity<T extends ArrayList&Comparable>{
    private T data;
    public void setData(T data){
        this.data = data;
    }
    public T getData(){
        return this.data;
    }
}

//jdk 编译之后,把T改成第一个ArrayList 如下
public class Genericity<ArrayList>{
    private ArrayList data;
    public void setData(ArrayList data){
        this.data = data;
    }
    public ArrayList getData(){
        return this.data;
    }
}