6.新特性-泛型

张开发
2026/4/22 17:25:59 15 分钟阅读
6.新特性-泛型
为什么会引入泛型泛型允许类、接口和方法在定义时使用一个或多个类型参数使得它们可以在编译时具有更强的类型检查并且能够避免类型转换错误。privatestaticintadd(inta,intb){System.out.println(ab(ab));returnab;}privatestaticfloatadd(floata,floatb){System.out.println(ab(ab));returnab;}privatestaticdoubleadd(doublea,doubleb){System.out.println(ab(ab));returnab;}如果没有泛型要实现不同类型的加法每种类型都需要重载一个add方法通过泛型我们可以复用为一个方法privatestaticTextendsNumberdoubleadd(Ta,Tb){System.out.println(ab(a.doubleValue()b.doubleValue()));returna.doubleValue()b.doubleValue();}泛型中的类型在使用时指定不需要强制类型转换类型安全编译器会检查类型ListlistnewArrayList();list.add(xxString);list.add(100d);list.add(newPerson());在使用上述list中list中的元素都是Object类型无法约束其中的类型所以在取出集合元素时需要人为的强制类型转化到具体的目标类型且很容易出现java.lang.ClassCastException异常。引入泛型它将提供类型的约束提供编译前的检查ListStringlistnewArrayListString();//list中只能放String, 不能放其它类型的元素泛型类//T仅仅是标记可以任意定义publicclassPointT{//var是T类型由外部传入privateTvar;publicvoidsetVar(Tvar){this.varvar;}publicTgetVar(){returnvar;}publicstaticvoidmain(String[]args){PointStringpnewPointString();p.setVar(jack);System.out.println(p.getVar().length());}}多元泛型//此处指定了两个泛型类型publicclassNotepadK,V{privateKkey;//此变量的类型由外部决定privateVvalue;//此变量的类型由外部决定privatevoidsetKey(Kkey){this.keykey;}privatevoidsetValue(Vvalue){this.valuevalue;}privateKgetKey(){returnthis.key;}privateVgetValue(){returnthis.value;}publicstaticvoidmain(String[]args){NotepadString,IntegertnewNotepad();t.setKey(jack);//设置第一个内容t.setValue(33);//设置第二个内容System.out.println(name: t.getKey());System.out.println(age: t.getValue());}}泛型接口//在接口上定义泛型publicinterfaceInfoT{// 定义抽象方法抽象方法的返回值就是泛型类型publicTgetVar();}// 定义泛型接口的子类classInfoImplTimplementsInfoT{//定义属性privateTvar;//通过构造方法设置属性内容publicInfoImpl(Tvar){this.setVar(var);}publicvoidsetVar(Tvar){this.varvar;}OverridepublicTgetVar(){returnthis.var;}publicstaticvoidmain(String[]args){InfoStringinfonewInfoImpl(JACK);System.out.println(info: info.getVar());}}泛型方法泛型方法是在调用方法的时候指明泛型的具体类型。说明一下定义泛型方法时必须在返回值前边加一个来声明这是一个泛型方法持有一个泛型T然后才可以用泛型T作为方法的返回值。Class的作用就是指明泛型的具体类型而Class类型的变量c可以用来创建泛型类的对象。为什么要用变量c来创建对象呢既然是泛型方法就代表着我们不知道具体的类型是什么也不知道构造方法如何因此没有办法去new一个对象但可以利用变量c的newInstance方法去创建对象也就是利用反射创建对象。泛型方法要求的参数是Class类型而Class.forName()方法的返回值也是Class因此可以用Class.forName()作为参数。其中forName()方法中的参数是何种类型返回的Class就是何种类型。在本例中forName()方法中传入的是User类的完整路径因此返回的是Class类型的对象因此调用泛型方法时变量c的类型就是Class因此泛型方法中的泛型T就被指明为User因此变量obj的类型为User。为什么要使用泛型方法呢因为泛型类要在实例化的时候就指明类型如果想换一种类型不得不重新new一次可能不够灵活而泛型方法可以在调用的时候指明类型更加灵活。泛型的上下限classA{}classBextendsA{}// 如下两个方法不会报错publicstaticvoidfunA(Aa){// ...}publicstaticvoidfunB(Bb){funA(b);// ...}// 如下funD方法会报错publicstaticvoidfunC(ListAlistA){// ...}publicstaticvoidfunD(ListBlistB){funC(listB);// Unresolved compilation problem: The method doPrint(ListA) in the type test is not applicable for the arguments (ListB)// ...}解决方案1.为了解决泛型中隐含的转换问题Java泛型加入了类型参数的上下边界机制。2.? extends A表示该类型参数可以是A(上边界)或者A的子类类型。3.编译时擦除到类型A即用A类型代替类型参数。4.这种方法可以解决开始遇到的问题编译器知道类型参数的范围如果传入的实例类型B是在这个范围内的话允许转换这时只要一次类型转换就可以了运行时会把对象当做A的实例看待。publicstaticvoidfunC(List?extendsAlistA){// ...}publicstaticvoidfunD(ListBlistB){funC(listB);// OK// ...}泛型上下限的引入在使用泛型的时候我们可以为传入的泛型类型实参进行上下边界的限制如类型实参只准传入某种类型的父类或某种类型的子类。上限publicclassInfoTextendsNumber{privateTvar;privateTgetVar(){returnthis.var;}privatevoidsetVar(Tvar){this.varvar;}publicStringtoString(){returnthis.var.toString();}publicstaticvoidmain(String[]args){InfoIntegeriinewInfoInteger();ii.setVar(33);System.out.println(ii.getVar());}}下限packagecom.heima.base.fanxing;publicclassInfoT{privateTvar;publicvoidsetVar(Tvar){this.varvar;}publicTgetVar(){returnthis.var;}publicStringtoString(){returnthis.var.toString();}publicstaticvoidmain(String[]args){//声明String的泛型对象InfoStringi1newInfoString();//声明Object的泛型对象InfoObjecti2newInfoObject();i1.setVar(jack);i2.setVar(newObject());fun(i1);fun(i2);}//只能接收String或Object类型的泛型String类的父类只有Object类publicstaticvoidfun(Info?superStringtemp){System.out.println(temp,);}}小结?无限制通配符?extendsEextends关键字声明了类型的上界表示参数化的类型可能是所指定的类型或者是此类型的子类?superEsuper关键字声明了类型的下界表示参数化的类型可能是指定的类型或者是此类型的父类// 使用原则《Effictive Java》// 为了获得最大限度的灵活性要在表示 生产者或者消费者 的输入参数上使用通配符使用的规则就是生产者有上限、消费者有下限1.如果参数化类型表示一个T的生产者使用?extendsT;2.如果它表示一个T的消费者就使用?superT3.如果既是生产又是消费那使用通配符就没什么意义了因为你需要的是精确的参数类型。

更多文章