当前位置: 首页 > 科技观察

看完让你变身武松,教你爪哇杀纸老虎

时间:2023-03-17 17:28:23 科技观察

泛型其实是Java中比较难的语法。源码,虽然看起来语法很难,但是当你看懂之后,你会发现它很简单。其实,它只是一只纸老虎。下面我就以一种非常简单易懂的方式带大家了解一下。相信你仔细看完一定会收获很多,再也不会害怕了!1.泛型的定义这里不用看网上的一些定义,因为相对于比较学术的,只要记住泛型可以在程序设计中指定某种类型,这样程序设计可以更标准化。之后,我们来讨论一下为什么要使用泛型语法,这个语法有什么作用?别担心,让我给你举个例子:}publicObjectget(){returnobjects[this.top-1];}}你看这是干什么的?这个我们自己写了一个栈,然后把栈里面的数组类型设置为Object类型,这样栈里面可以存任何类型的数据(Object类是任何类的父类,不管是什么类型的数据被插入,可以发生向上转换)现在,让我们测试它:publicclassTest{publicstaticvoidmain(String[]args){Stackstack=newStack();stack.push(1);stack.push(2);stack.push("123");Stringstr=(字符串)堆栈。上面写的类型转换可以在栈中存储任何类型的数据。它更通用,它的优点也可以变成缺点。因为过于笼统,降低了代码的规范性,看起来比较乱。这时候,我们可以考虑使用泛型,让具体的数据可以存储在类或者Java集合中(使用Java集合时,一般都会使用泛型,自定义类型可以使用也可以不使用泛型)3.泛型的写法类型的方法以自定义类型为例。写法是在类名后面加尖括号,里面写一个字母(注意这里可以写任何字母,只是起到标志这个类是泛型类的作用)classStacknew对象时,例如只能将整数存储在堆栈中。尖括号中的尖括号必须写基本数据类型对应的包装类,后面的尖括号可以省略。例子如下:Stackstack=newStack<>();在Java中组成基本数据类型和对应的封装类:因此,我们上面写的自定义栈可以写成如下形式(以整数的存储为例):classStack{publicT[]objects;publicinttop;publicStack(){this.objects=(T[])newObject[10];}publicvoidpush(Tobj){objects[this.top++]=obj;}publicTget(){returnobjects[this.top-1];}}Stackstack=newStack<>();stack.push(1);stack.push(2);intret=stack.get();System.out.println(ret);这里要特别注意:publicStack(){this.objects=(T[])newObject[10];}这里不能写成this.objects=newT[10];原因:泛型数组不能是新的。也可以理解为先检查泛型,再编译。如果一个新的泛型类型的数组是new的,编译器在检查T是什么类型的时候并不知道,所以会报错擦除机制只会在编译的时候用到,会转为Object类型。因为有这种擦除机制,所以这里可以对整个数组进行cast(一般情况下,数组不能整体进行cast),因为泛型只在编译时起作用,但在实际运行时会被擦除成Object类型,即就是在实际运行时没有泛型的概念,即实际运行时类型是一样的,所以T本质上是对象类型。所以这段代码相当于没有强制类型转换直接指定泛型类型(不是T)的代码!!!打印结果(不打印类型):这里注意理解。四、泛型的使用示例1、求最大值是泛型的一个重要知识点,但是光看还不够,还是要通过示例让大家有更深刻的理解。例如,如何编写泛型类来查找数组的最大值?基本框架大概是这样的:(不懂的请仔细阅读以上内容)classAlgorithm>{publicTfindMax(T[]array){Tmax=array[0];for(inti=1;i>{publicTfindMax(T[]array){Tmax=array[0];for(inti=1;i>注意extends称为上界,这段代码的意思是将泛型类T擦除到实现Comparable接口的地方。换句话说,必须实现T类型与Comparable接口相同的推理:这段代码publicclassMyArrayList{...}表示E是Number或Number本身的子类。让我们在下面使用它:Algorithmalgorithm1=newAlgorithm<>();Integer[]integers={1,2,13,4,5};Integerret=algorithm1.findMax(integers);System.out.println(ret);运行结果如下:成功!2.优化经过上面的努力,我们写了一个泛型类,用于求一个数组的最大值,但是上面的例子是一个整型数组,那么我们是否可以在数组中存入其他类型进行比较呢?答案是肯定的,但是我们还是得去new一个对象,例如:Algorithmalgorithm2=newAlgorithm<>();这很麻烦但是我们可以把求最大值的方法设置成一个静态类Algorithm2,因为它是静态方法,不需要new一个对象,所以在new一个对象的时候没有指定泛型类型的过程,所以有方法后面不需要加尖括号,但是去掉之后,代码又会出错:我们可以这样修改:classAlgorithm2{publicstatic>TfindMax(T[]array){Tmax=array[0];for(inti=1;iTfindMax(T[]array){}调用了pan类型的方法继续带大家去使用:publicstaticvoidmain(String[]args){Integer[]integers={1,2,13,4,5};//根据形参的类型推导整个泛型ParametersIntegerret=Algorithm2.findMax(integers);System.out.println(ret);Integerret2=Algorithm2.findMax(integers);System.out.println(ret2);}注意ret1写成in和ret2一样,可以打印结果如下:五、通配符1.基本写法通配符也是泛型的一种,我们写一个泛型的方法来打印集合中的元素。classTest{publicstaticvoidprint(ArrayListlist){for(Tt:list){System.out.println(t);}}这个写法很简单,上面说了,那我们试试吧它:publicstaticvoidmain(String[]args){ArrayListlist=newArrayList<>();list.add(1);list.add(2);list.add(3);Test.print(list);}打印结果如下:除了上述的写法,我们还可以改成通配符的写法。让我先给你看代码://?表示通配符擦除机制Objectpublicstaticvoidprint2(ArrayListlist){for(Objectt:list){System.out.println(t);}}}这里for(Objectt:list)必须这样写,因为通配符也exist擦除机制会在编译器中对Object类型进行编程。2.上界语法:例子:publicstaticvoidprintAll(MyArrayListlist){...}xxxxxxxxxxbrpublicstaticvoidprintAll(MyArrayListlist){br...br}表示可以传入类型参数为Number子类的任何类型的MyArrayList,所以下面的调用都是正确的:printAll(newMyArrayList());printAll(newMyArrayList());printAll(newMyArrayList());xxxxxxxxxxbrprintAll(newMyArrayList());brprintAll(newMyArrayList());brprintAll(newMyArrayList());下面的调用都是错误的:printAll(newMyArrayList());printAll(newMyArrayList());xxxxxxxxxxbrprintAll(newMyArrayList());brprintAll(newMyArrayList());3.下限下限和上限的用法非常相似语法:示例:publicstaticvoidprintAll(MyArrayListlist){...}xxxxxxxxxxxbrpublicstaticvoidprintAll(MyArrayListlist){br...br}表示任意类型的MyArrayList,其实参是一个Integer父类,所以下面的调用是正确的:printAll(newMyArrayList());printAll(newMyArrayList());printAll(newMyArrayList());xxxxxxxxxxbrprintAll(newMyArrayList());brprintAll(newMyArrayList());brprintAll(newMyArrayList());下面的调用是错误的:printAll(newMyArrayList());printAll(newMyArrayList());xxxxxxxxxxbrprintAll(newMyArrayList());brprintAll(newMyArrayList());6、学习了泛型的局限性后,我们在使用泛型的过程中要注意以下限制:泛型类型参数不支持基本数据类型不能实例化泛型类型的对象不能使用泛型类型声明的静态属性不能使用instanceof判断带有类型参数的泛型类型(因为它们被擦除机制擦除)无法创建泛型类数组无法创建、捕获、抛出泛型类异常(异常不支持泛型)泛型类型不是形式参数的一部分,所以它不能超载。这次通用知识点的分享就到此结束了。整理不易,但如果能对大家有所帮助,我很高兴,也希望大家多多理解。无论是入门学习还是复习,都值得细细琢磨!我们一起工作吧!