目录
  • 1.什么是泛型
  • 2.引出泛型
  • 3.泛型类的语法
  • 4.裸类型
  • 5.泛型如何编译的
    • 5.1 擦除机制
    • 5.2.泛型数组为什么不能实例化
  • 6.泛型的上界
    • 7.通配符
      • 7.1.通配符能用来干嘛
      • 7.2.通配符的上界(读数据)
      • 7.3.通配符的下界(写数据)

    1.什么是泛型

    泛型是在JDK1.5引入的新的语法,通俗讲,泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化。

    看到标红的这句话,有些兄弟可能会有疑惑,我们以前传参,传得是一个整形数据,传得是一个引用,我从来没传过类型嘛,类型难道还能作为参数传递???对,没错,接下来,我们就来看一下类型作为参数传递的实现方式

    2.引出泛型

    我们以前学过继承和多态,我们知道所有类的父类默认是Object类,我们现在目标是:数组中可以存放任何类型的数据,这时候我们就可以new一个Object类型的数组,,,

    class MyArray {
        public Object[] array = new Object[10];
        public Object getPos(int pos) {
            return array[pos];
        }
        public void setPos(int pos, Object value) {
            array[pos] = value;
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            MyArray myArray = new MyArray();
            myArray.setPos(0,1);
            myArray.setPos(1,"hello");
        }
    }

    当我们写出这样一个代码的时候,我们发现数组中什么元素都能放了,可以放整形数据,可以放字符串,还可以放小数,这里面能放的东西太多了,你能知道某个下标的元素是什么样的类型吗??当我们取元素的时候,会发现你的代码编译都不能通过,,,

    Java通俗易懂讲解泛型

    为什么会出现这样的情况呢,我相信大部分兄弟们都知道,我们观察getPos的返回值是Object,也就意味着我们拿出来的可能是任意元素,但编译器知道吗???它只知道返回的是一个Object:这就很明显了,父类给子类,,这不纯纯的向下转型吗,向下转型就必须要强转类型,否则你的代码就不可能通过。

    那么问题又来了,,我这里只是放了两个元素,肉眼还能看的过来,但是这里面的类型时刻在发生改变,就算你知道类型,你每次都需要强转吗,我是动态的啊,我代码是写死的啊,所以这个地方如果用Object去做的话,会显得格外麻烦,而且局限性非常的大

    我们现在的困境有两个:

    1.任何类型都可以放,不好控制,,,

    2.每次取元素的时候,都得进行强制类型转换,,,

    这时候我们得搞一手希望工程,我们的希望是:

    1.我们能不能自己指定类型

    2.我们能不能,不再进行类型转换呀,能不能把这一步省略呀,兄弟!!!

    为了解决这一问题,我们java就引入了这个东西—-》泛型

    那到底怎么指定类型呢,来看代码:

    class MyArray <T>{
        //public Object[] array = new Object[10];
        public T[] array = (T[]) new Object[10];//这样写也不好,待会说为什么??
        public T getPos(int pos) {
            return array[pos];
        }
        public void setPos(int pos, T value) {
            array[pos] = value;
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            //我指定放整形
            MyArray<Integer> myArray = new MyArray<>();
            //后面的尖括号里的类型可以省略,在实例化对象的时候根据前面可以做出判断
            //MyArray<Integer> myArray = new MyArray<Integer>();
            myArray.setPos(0,1);
            myArray.setPos(1,2);
            Integer ret = myArray.getPos(1);
            System.out.println(ret);
            //我指定放字符串类型
            MyArray<String> myArray2 = new MyArray<>();
            myArray2.setPos(0,"abc");
            myArray2.setPos(1,"bit");
            String ret2 = myArray2.getPos(0);
            System.out.println(ret2);
        }
    }

    这个代码有几个注意事项:

    1.尖括号<>里面只能放引用类型;

    2.<T>是一个占位符,代表当前类是一个泛型类;

    3.MyArray<Integer> array = new MyArray<>();后面的尖括号可以省略Integer不写,实例化对象的 时候编译器根据前面能做出判断。

    看完上面的代码,我相信兄弟们已经明白是怎么一回事了吧

    我们刚刚的希望工程也已经实现了

    1.<Integer>,指定当前类中,使用的类型是Integer类型

    2.泛型帮我在编译期间做了2件事情(目前为止的优点):

    • 存放元素的时候,进行类型的检查
    • 取元素的时候,帮我进行类型的转换

    3.泛型类的语法

    有了刚刚知识的铺垫,这一块咱就形式化一下啦,,当然,语法还是很重要滴!!

    语法:

    泛型类<类型实参> 变量名; // 定义一个泛型类引用 
    new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象

    栗子:

    MyArray<Integer> list = new MyArray<Integer>();

    4.裸类型

    说明:

    兄弟们,咱学了新东西,这旧东西,咱也得了解一下是不咯,,万一以后跟别人聊到了,自己好像从来没听过就尴尬了,,

    裸类型是一个泛型类但没有带着类型实参,例如 MyArrayList 就是一个裸类型

    MyArray list = new MyArray();

    注意: 我们不要自己去使用裸类型,裸类型是为了兼容老版本的 API 保留的机制

    5.泛型如何编译的

    5.1 擦除机制

    我用的是Powershell窗口查看的字节码文件,当然用idea的兄弟们也可以去下载一个展示字节码的插件,,接下来我们看看泛型到底是怎么编译的,,javap -c查看字节码

    Java通俗易懂讲解泛型

    我们发现在编译期间,所有的T都被擦除为了Object,那既然所有的T都变成了Object,那我们为什要指定类型呢?注意这里不是编译的时候替换,指定类型只是为了在编译的时候帮我们进行类型的检查和转换,并不是替换!!!最终字节码编译完成的时候,所有的T变成了Object,而这就是我们Java当中泛型所谓的擦除机制!!!

    提问:那既然T在编译的时候被擦除为了Object,那为啥 T[] array = new T[] 会报错呢?

    5.2会给你答案

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。