带你深入理解泛型

发布于:2022-12-22 ⋅ 阅读:(445) ⋅ 点赞:(0)

目录

前言:

泛型类的使用

擦除机制

泛型上界

静态泛型方法

非静态泛型方法

通配符

通配符上界

通配符下界

小结:


前言:

😉Java和其他语言一样,都支持泛型,包括泛型类和泛型方法。泛型的出现使得类型参数化,Java提出了擦除机制,如果没有声明上界class Test<T>,最终将Test类都擦除Object类。如果声明上界class Test<T extends Number>意味着只能传递Number类或者Number的子类,最终擦除成Number类。这些动作都是在编译期间完成的,编译时会自动进行类型检查和转换,运行时没有泛型的概念。

泛型类的使用

class Test<T>{
    private T[] tmp = (T[])new Object[10];

    public void setTmp(T tmp, int pos) {
        this.tmp[pos] = tmp;
    }

    public T getTmp(int pos) {
        return tmp[pos];
    }
}
public class Test17 {
    public static void main(String[] args) {
        Test<Integer> test = new Test<>();
        test.setTmp(10,0);
        int a = test.getTmp(0);
        Test<String> test2 = new Test<>();
        test2.setTmp("aaa",0);
        String b = test2.getTmp(0);
        System.out.println(a + " " + b);
    }

}

😆泛型类中是不可以直接new数组(T[] arr = new T[10]),因为在擦除机制中会将T擦成Object类,而Object数组可以存放存放多种类型数据,编译器认为这是不安全的,直接在编译期间就阻止了。T[] tmp = (T[])new Object[10]这样的写法编译器也会报警告,毕竟Object数组不是T类型的数组,除非T刚好擦除成Object。

😆test.setTmp(10,0)   test2.setTmp("aaa",0) 在编译期间会根据Integer和String进行类型检查,直接阻挡在编译期间。int a = test.getTmp(0)   String b = test2.getTmp(0) 就是自动拆包的过程。

擦除机制

 🎉通过javap -c查看字节码文件,可以清楚看见将T擦除为Object

泛型上界

class Test<T extends Number>{
    private T[] tmp = (T[])new Object[10];

    public void setTmp(T tmp, int pos) {
        this.tmp[pos] = tmp;
    }

    public T getTmp(int pos) {
        return tmp[pos];
    }
}
public class Test17 {
    public static void main(String[] args) {
        Test<Integer> test = new Test<>();
        test.setTmp(10,0);
        int a = test.getTmp(0);
    }
}

🎄可以指定泛型类的上界,同时也就限制了传递参数的类型,只能传递Number的子类,编译时会将T擦除成Number,否则不能通过编译 。

静态泛型方法

class Test{
    public static<T extends Comparable<T>> void fun(T a, T b) {
        if(a.compareTo(b) > 0) {
            System.out.println(a);
        }else {
            System.out.println(b);
        }
    }
}
public class Test17 {
    public static void main(String[] args) {
        Integer a = 10;
        Integer b = 20;
        Test.fun(a, b);
    }
}

🐵静态泛型方法不依赖于对象,直接通过类名就可以调用,因此类型的传参是体现在方法中,当使用者调用方法时,在编译期间会进行类型检查和转换。擦除机制和前面擦除机制是一致的。

非静态泛型方法

class Test{
    public <T extends Comparable<T>> void fun(T a, T b) {
        if(a.compareTo(b) > 0) {
            System.out.println(a);
        }else {
            System.out.println(b);
        }
    }
}
public class Test17 {
    public static void main(String[] args) {
        Integer a = 10;
        Integer b = 20;
        Test test = new Test();
        test.fun(a, b);
    }
}

🧢非静态泛型方法就得依赖对象,直接通过对象调用即可,其他和静态泛型方法一致。

通配符

🪖在使用泛型类实例化对象后,这个被作为参数传递时,由于泛型对象中可能会有多种不同类型,那么形参类型就无法确定。?符号就解决了这个问题,可以接收任何类型的参数。同时也可以指定通配符的下界和上界。

通配符上界

class Test<T> {
    private T date;
    public void setDate(T date) {
        this.date = date;
    }
    public T getDate() {
        return this.date;
    }
}
public class Test17 {
    public static void fun(Test<? extends Number> tmp) {
        Number a = tmp.getDate();
        System.out.println(a);
    }
    public static void main(String[] args) {
       Test<Integer> test = new Test<>();
       test.setDate(10);
       fun(test);
    }
}

🎈擦除机制会将tmp类型擦除成Number,因此只能传递Number的子类。由于tmp类型会有多种,所以不可以在fun中设置成员变量的值(date)。可以获取date,它们都有一个共同的父类

通配符下界

class Test<T> {
    private T date;
    public void setDate(T date) {
        this.date = date;
    }
    public T getDate() {
        return this.date;
    }
}
public class Test17 {
    public static void fun(Test<? super String> tmp) {
        tmp.setDate("aaa");
    }
    public static void main(String[] args) {
       Test<String> test = new Test<>();
       fun(test);
       System.out.println(test.getDate());
    }
}

😯指定通配符下界,只能传递String类的父类,由于父类会有多种,不能获取到tmp中成员变量(date),因为获取后无法指定一个类型去接收。可以设置date,tmp是String或者String的父类

小结:

🐵泛型概念是在Java5后出现,使得代码更加灵活,需深入理解,使我们写代码更加有设计感。


网站公告

今日签到

点亮在社区的每一天
去签到