当前位置: 首页 > 后端技术 > Java

说说Java的泛型

时间:2023-04-02 01:49:39 Java

在Java没有泛型之前,只能使用Object变量,在需要的地方使用强制类型转换,但是这样会导致代码不安全和可读性差。因此,Java5开始支持泛型。使用泛型编写的代码可以被许多不同类型的对象重用。定义泛型泛型的本质是参数化类型。所谓参数化类型是指用来声明数据的类型本身,也可以改变,由实际参数决定。泛型:将类型定义的工作推迟到创建对象或调用方法以指定特定类型时。在泛型类下面定义了一个简单的泛型。公共类Holder{privateTa;publicGenericHolder(){}publicvoidset(Ta){this.a=a;}publicTget(){返回一个;}publicstaticvoidmain(String[]args){Holderv=newHolder();v.set(1024);//这里有类型校验Integera=v.get();//不需要类型转换}}上面的创建在创建Holder实例的时候,需要指定持有对象的类型,然后Holder实例只能存储该类型或其子类的值。调用get()获取值时,获取的是该类型的值。类定义中的类型变量是指方法的返回类型以及字段和局部变量的类型。Holderv=newHolder()在Java5中写成,在Java7中写成Holderv=newHolder<>(),称为“菱形语法”。写泛型时,需要定义泛型类型;静态方法不能引用泛型类型,必须定义其他类型(如)来实现静态泛型方法;泛型可以同时定义多个类型,例如:Map。注意:类型变量大写和缩写很常见。在Java库中,用变量E表示集合的元素类型,K和V分别表示表的键和值类型。T(以及适当的相邻字母U和S)表示“任何类型”。泛型方法下面定义了一个带有类型参数的泛型方法。泛型位于方法的修饰符和它的返回值之间。publicstaticSetunion(Sets1,Sets2){Setresult=newHashSet<>(s1);结果.addAll(s2);returnresult;}上面类的静态方法独立于类改变方法,比把类泛型化更清晰易懂。对于泛型类,在实例化类时必须指定类型参数。使用泛型方法时,通常不需要指定参数类型,因为编译器会找出它们。这称为类型参数推断。如果要在参数中使用可变参数,则会创建一个数组进行存储;这个数组是一个实现细节并且是可见的。因此,当可变参数具有泛型或参数化类型时,编译警告信息会出现混淆。之前,使用SuppressWarnings("uncheck")注释来抑制警告。从Java7开始,添加了@SafeVarargs注解以保证声明的方法是类型安全的并消除警告。@SafeVarargspublicstaticListmakeList(T...args){Listresult=newArrayList<>();对于(T项目:args){结果。新增项目);}returnresult;}类型限定对数组进行排序时,可以实现排序方法:publicclassSortimplementsSerializable{publicvoidsort(T[]source){for(inti=1;i&Serializable>implementsSerializable{publicvoidsort(T[]source){for(inti=1;i是一种类型限制的方式。其中,BoundingType是父类,T代表BoundingType类型的子类型。它的边界是[T,BoundingType]并且T和BoundingType都在边界内。一个类型变量可以有多个限定条件,限定条件之间用&隔开。通配符类型的泛型使用起来更方便,但是参数化类型不变,在Holder类中添加比较方法时。publicbooleantoEquals(Holderobj){returnthis.get()==obj.get();}这种方式创建的对象比较时只能比较相同的类型。Holdera=newHolder<>();a.set(100);Holderb=newHolder<>();b.set(200);a.toEquals(b);//正确的Holderc=newHolder<>();c.set(123.32);a.toEquals(c);//ErrorJava提供了一种特殊的参数化类型,称为受限通配符类型,可以处理类似的情况。如下:publicbooleantoEquals(Holderobj){returnthis.get()==obj.get();}该方法的参数化类型是Number的子类。a.toEquals(c);//True因此,上述方法将编译无错。您还可以指定超类型限定的通配符,如下所示:superT这个意思是T或者T的超类型。所以上面的方法换一种写法:publicbooleantoEquals(Holderobj){returnthis.get()==obj.get();}的这个方法的参数化类型是Integer的父类。即Integer和Number都可以,与Double值比较会报错。Holdera=newHolder<>();a.set(100);Holderb=newHolder<>();b.set(2003.02);a.toEquals(b);//正确的Holderc=newHolder<>();c.set(123.23);a.toEquals(c);//无限通配符也可以用于报错,比如Holder,和Holder是一样的extendsObject>是一样的。因此,为了获得最大的灵活性,请在参数类型上使用通配符类型。以下助记符可以方便您记住要使用的通配符类型:PECSforproducer-extends,consumer-super:use如果参数化类型表示生产者T;如果它代表消费者或T,请使用。泛型擦除虚拟机没有泛型类型对象,所有对象都属于普通类。因此,最后定义的泛型类型将被擦除并替换为限定类型,非限定类型的变量将替换为Object。公共类Holder{privateTa;publicHolder(){}publicvoidset(Ta){this.a=a;}publicTget(){返回一个;}}T是无界变量,直接用Object代替。如果给出限定,则原始类型将替换为第一个限定类型变量。publicclassHolderimplementsSerializable{privateTa;publicHolder(){}publicvoidset(Ta){this.a=a;}publicTget(){返回一个;}}在执行类型擦除时,原始类型Holder如下所示:publicclassHolderimplementsSerializable{privateComparablea;publicHolder(){}publicvoidset(Comparablea){this.a=a;}publicComparableget(){返回一个;}}这也说明Java提供的List等集合类,无论使用时设置为List还是List,在运行时都是同一类型,都会被擦除成native类型列表。歧义错误泛型的引入增加了歧义错误的可能性。如果泛型类Holder中的两个泛型声明在擦除后变为同一类型并导致冲突,则会发生歧义错误。公共类Holder{Uvar1;变量2;//过载错误publicvoidset(Uu){var1=u;}//过载错误publicvoidset(Ss){var2=s;}}当声明两个相同的类型时,它会导致set()方法的两个版本相同。此外,对set()方法进行类型擦除会使两个版本都设置为set(Objecto),这也会导致错误。限制原始类型类型参数不能替代原始类型。比如Holder中的泛型T只能是Object类及其子类,不能是Java中的基本类型。类型查询不能使用instanceof来查询对象是否属于泛型类型。if(ainstanceofHolder)//错误同样,getClass()方法返回原始类型。Holders=newHolder<>();s.getClass();//类xx.xx.HolderHolderi=newHolder<>();i.getClass();//类xx。xx.Holder类型变量不能使用泛型来实例化变量。使用newT(...)、newT[...]或T.class是错误的。泛型只是一个占位符。类型变量在泛型类的静态上下文中无效静态成员不能使用类中声明的类型参数,但可以声明静态泛型方法publicclassHolder{//errorprivatestaticTt;//错误publicstaticTgetT(){returnt;}//正确publicstaticvoidtest(Kk){}}泛型数组不能实例化参数化类型的数组。如下:Holder[]holders=newHolder<>[10];上面的方法是不允许的,但是声明一个Holder[]类型的变量是合法的。通用类型的实例通用类型的实例既不能被抛出也不能被捕获。事实上,泛型类扩展Throwable甚至是不合法的。//错误:通用类不能扩展'java.lang.Throwable'publicCustomExceptionextendsException{}Catch子句不能使用类型变量。例如,以下方法将不会编译:publicstaticvoiddoWork(Classt){try{//dowork}catch(Te){//错误——无法捕获类型variable}}然而,在异常规范中使用类型变量是允许的。以下方法是合法的:publicstaticvoiddoWork(Tt){try{//dowork}catch(Throwablee){t.initCause(e);扔t;}}欢迎关注公众号《海人志》,期待与您共同进步!