java11_java11-泛型及其使用

1.概述

就本质而言 “泛型”的意思就是参数化类型。参数化类型很重要,因为使用该特性创建的类、接口以及方法可以以参数的形式指定操作的数据类型。

泛型通俗的说就是方法的返回值或参数是不确定的,可以随创建该类对象时改变而改变。

泛型提供了以前缺失的类安全性,并且还可以简化处理过程(例如避免进行强制类型转换等),提高了代码的可重用性。

2.一段简单的代码理解泛型

对于下面这段代码我们定义了一个泛型类,类中有一个私有的泛型成员变量和几个泛型方法。从中我们可以看出,成员变量或者方法的参数或者返回值可能不是肯定的而是T,并且T是可变的,我们称这个T为泛型。

main方法中分别使用这个泛型类操作了String 和 Integer 。如果没有泛型的支持,我们就只能写两个逻辑相同而参数不同的类或者进行强制转换,大大降低了代码重用和安全性。

1 class MyGeneric{ //声明一个泛型类

2 private T obj;//可以使用泛型指定变量

3 public MyGeneric() {}; //无参构造

4 public MyGeneric(T val) { //泛型构造

5 this.obj =val;6 }7 public T getObj() { //返回值

8 return this.obj;9 }10 public String getName() { //得到确切类型

11 return this.obj.getClass().getName();12 }13

14 }15

16 public classTestGeneric {17 public static voidmain(String[] args) {18 MyGeneric str = new MyGeneric("xiaobai"); //操作String

19 System.out.println(str.getObj());20 System.out.println(str.getName());21

22 MyGeneric integer = new MyGeneric(5); //操作Integer

23 System.out.println(integer.getObj());24 System.out.println(integer.getName());25 }26 }

3.几点注意

1.泛型只是一个占位符,并没有实际意义,对于上面的例子,实际上操作的还是相应的数据类型(String和Integer)

2.泛型只能使用引用类型,不支持基本数据类型(至于上面第22行可以直接写5是因为自动装箱)

3.泛型引用之间是不兼容的,比如上面例子中的代码 str和integer两个实例对象是不兼容的(这是废话,泛型的目的就是提高通用性并限制操作类型)

4.多个泛型

java中允许一个类有多个泛型,泛型之间使用逗号隔开即可(参看Map)。并且泛型只是一个占位符,可以用任何字符表示

1 class ManyGeneric{2 privateT obj1;3 privateV obj2;4 privateW obj3;5 publicT getObj1() {6 returnobj1;7 }8 public voidsetObj1(T obj1) {9 this.obj1 =obj1;10 }11 publicV getObj2() {12 returnobj2;13 }14 public voidsetObj2(V obj2) {15 this.obj2 =obj2;16 }17 publicW getObj3() {18 returnobj3;19 }20 public voidsetObj3(W obj3) {21 this.obj3 =obj3;22 }23

24 }

5.泛型方法

如果我们只想在一个方法中使用泛型,换句话说:如果类或者接口没有定义泛型,但是其中的某个或几个方法需要使用泛型(比如静态方法接受一个泛型参数等)该怎么解决? 答案就是泛型方法。

定义泛型方法的语法是: 访问修饰符 方法返回值 方法名称(形参列表) {  方法体  }

eg: public static mygeneric(T t) {xxx}

泛型方法的定义和普通方法定义不同的地方在于需要在修饰符和返回类型之间加一个泛型类型参数的声明,表明在这个方法作用域中谁才是泛型类型参数;

泛型方法与泛型类的区别:

class A{

//泛型的作用域是整个类

}

classA{public static print(T t){ //该泛型只作用于该方法上

sout(t);

}

}

6.泛型有界类型及泛型通配

6.1 泛型通配

不显式第指定泛型类型,而是使用 ?来表示泛型。当我们无法立即确定该类引用使用哪一个泛型(或至少可以确定他与某一个类的关系时)就使用这种形式。该通配一共有三种形式

1.  > 形式: 不知道泛型到底是何形式·:使用这种方式时  就相当一    引用的实际对象只要是Object的子类就可以

ArrayList> list = new ArrayList (); //正确

ArrayList> list = new ArrayList (); //正确

2. extends xxx> 不知道具体是哪一种形式,但一定是xxx的子类  :引用的实际对象只能是 xxx类或xxx类的子类

ArrayList extends Number> list = new ArrayList (); //错误 不是Number子类

ArrayList extends Number> list = new ArrayList (); //正确

3. super xxx> 不确定哪一种形式,但一定是xxx的父类:引用的实际对象只能是  xxx 或 xxx的父类

ArrayList super Integer> list = new ArrayList (); //正确

ArrayList super Integer> list = new ArrayList (); //正确

ArrayList super Integer> list = new ArrayList (); //错误 不是Integer的父类

6.2 有界类型

可以使用任意参数来替换泛型类型是很好的,但是有一些时候我们可能希望对泛型类型进行一些限制。例如我们想在泛型中取得每一个值得double值,这个只有在类继承Number类才可以实现,否则就会编译错误。

那么这个时候我们就可以使用有界类型进行限定,要求泛型类型必须是Number类或其子类。

语法: T extends superClass  意思是说只允许superClass或者其子类的泛型。

见如下例子:

错误的代码:

1 class MyMathClass{2 T[] nums;3 publicMyMathClass(T[] o) {4 this.nums =o;5 }6 public doubleavg() {7 double sum = 0.0;8 for(int i=0;i

10 }11 return sum/nums.length;12 }13 }14

15 public classTestGeneric {16 public static voidmain(String[] args) {17 Integer[] nums = new Integer[10];18 for(int i=0;i<10;i++) {19 nums[i] = (int)(Math.random()*100);20 }21 MyMathClass test = new MyMathClass(nums);22 test.avg();23 }24 }

这里的代码第9行报错,因为不是所有的类型都有doubleValue方法。但是只要是继承了Number类就会有这个方法,所以我们使用有界类型进行限定

限定方式  T extends Number

1 class MyMathClass{2 T[] nums;3 publicMyMathClass(T[] o) {4 this.nums =o;5 }6 public doubleavg() {7 double sum = 0.0;8 for(int i=0;i

10 }11 return sum/nums.length;12 }13 }14

15 public classTestGeneric {16 public static voidmain(String[] args) {17 Integer[] nums = new Integer[10];18 for(int i=0;i<10;i++) {19 nums[i] = (int)(Math.random()*100);20 }21 MyMathClass test = new MyMathClass(nums);22 System.out.println(test.avg());23 }24 }

同理还可以使用多边界限定,当有多个边界的时候 使用&进行连接 :class MyMathClass 意思就是说只允许Number或其子类类型并且必须实现Serializable接口。

注意有界类型与泛型通配的区别 (有界类型是声明泛型类指定泛型范围,而泛型通配是泛型引用指向泛型对象时的限制)

7.泛型擦除

由于要与以前的代码相兼容,java中的泛型是伪泛型,在编译器编译过程中将会擦除泛型的所有信息,使用确切的引用类型将其代替。这就意味着如果没有显示的确定类型,就将使用Object代替,然后进行强制转换来保证类型的匹配

这里就会发生一个模糊性问题,当有两个重载方法时,并且该方法唯一区别就是参数类型不同,则可能造成错误:

cebc70573cbbaac83716934dc65a190f.png

8. 使用泛型的一些限制

8.1 不能实例化泛型参数

T  obj = new T(); 是不合法的,因为编译器不知道要创建哪一种类型的对象,T只是一个占位符。

8.2 静态成员限制

静态成员不能使用泛型声明参数,同时,静态方法也不能操作泛型参数,因为静态属性需要随类加载,无法判断当前泛型的类型。

8.3 数组的限制

1.不能实例化泛型数组  原因参考7.1

2.不能声明指向特定泛型类型的数组

33d5150bea74a82018530b7dbaaceefd.png

9.泛型对异常的限制

泛型类不能继承Throwable类,这就意味着不能创建泛型异常类(也就是说泛型不支持异常,但是泛型类中依旧支持其他类型的异常)

当然,泛型支持异常也没有什么意义。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值