0


Java全栈知识(5)泛型机制详解

1、为什么要引入泛型

泛型的引入是为了实现类型的参数化,使得我们在创建函数但是需要调用者来指定使用的类型的时候,不用使用Object类型。而泛型被作用于类,接口,方法中。这些也被叫做泛型类,泛型接口,泛型方法。
泛型的好处:

1.1、提高了代码的复用性,

如果不使用泛型,下列代码应该这样写:

privatestaticintadd(int a,int b){System.out.println(a +"+"+ b +"="+(a + b));return a + b;}privatestaticfloatadd(float a,float b){System.out.println(a +"+"+ b +"="+(a + b));return a + b;}privatestaticdoubleadd(double a,double b){System.out.println(a +"+"+ b +"="+(a + b));return a + b;}

但是通过泛型我们就可以减少代码的冗余程度:

privatestatic<TextendsNumber>doubleadd(T a,T b){System.out.println(a +"+"+ b +"="+(a.doubleValue()+ b.doubleValue()));return a.doubleValue()+ b.doubleValue();}

1.2、类型安全

如果我们使用List集合而不使用泛型的时候,取出的元素都是Object类型,需要我们认为进行元素强制类型转换,转换到目标类型。但是这个过程中就容易出现java.lang.ClassCastException异常,
而且,例如不使用泛型的Arraylist的元素类型都是Object,无法约束其中的元素类型。引入泛型将进行编译前类型检查。约束了其中可以存放元素的类型,取出时也不用进行强制类型转换。

2、泛型的上下限

泛型上限:

publicstaticvoidfunC(List<?extendsA> listA){// ...          }

泛型上限使用extends完成,编译器在编译的时候会把泛型擦除为A类型,所以我们可以传入A的子类或A类型不报错。
泛型下限:

publicstaticvoidfun(Info<?superString> temp){//...}

下限规定之后,只能接收String或Object类型的泛型,String类的父类只有Object类

3、泛型擦除机制

泛型机制是JDK1.5引入的新内容,所以说需要与老的代码兼容,所以说Java实现的其实是一种”伪泛型“的机制,即在编译期会把<>里的内容全部擦去,替换为具体的类型。
替换规则:
总会替换为当前范围内上限最高的类型,例如:

  • 如果没有限制的时候,将替换为最高的Object;
  • 如果<? extends A>的时候,将替换为A;
  • 如果是<?super String>的时候,将替换为Object

image.pngimage.png

验证泛型擦除机制:

publicclassTest{publicstaticvoidmain(String[] args){ArrayList<String> list1 =newArrayList<String>();
        list1.add("abc");ArrayList<Integer> list2 =newArrayList<Integer>();
        list2.add(123);System.out.println(list1.getClass()== list2.getClass());// true}}

4、泛型的编译器检查

我们上面说了泛型会有擦除机制,会把所有的擦除改变为Object。那为什么我们在往ArrayList里面添加整数会报错呢?

Java编译器是先检查了代码中的泛型类型,在进行泛型的擦除,再进行编译。

publicstaticvoidmain(String[] args){ArrayList<String> list =newArrayList<String>();  
    list.add("123");  
    list.add(123);//编译错误  }

根据这段代码我们就能看出来,泛型检查就在擦除前。
我们在写的时候会出现下面两种情况:

ArrayList<String> list1 =newArrayList();//第一种 情况ArrayList list2 =newArrayList<String>();//第二种 情况

然后我们分别往两个list中添加整形元素,我们发现list1在添加的时候编译报错,但是list2却能正常的添加。
这是因为本质上两个list都是new了一个ArrayList对象,并没有。而泛型的类型检查是通过对象的类型进行的编译前检查,从而保证类型安全。

5、泛型的桥接方法

类型擦除会造成多态的冲突,而JVM解决方法就是桥接方法。

如果我们有这么一个泛型类:

classPair<T>{privateT value;publicTgetValue(){return value;}publicvoidsetValue(T value){this.value = value;}}

有一个子类去继承他,并且指定泛型类型:

classDateInterextendsPair<Date>{@OverridepublicvoidsetValue(Date value){super.setValue(value);}@OverridepublicDategetValue(){returnsuper.getValue();}}

我们可以看到在子类中我们覆盖了弗雷德两个方法,但是因为泛型擦除的机制,编译的时候父类方法中的泛型都被擦除为Object。此时的状态就会变成下面这样:

classPair{privateObject value;publicObjectgetValue(){return value;}publicvoidsetValue(Object  value){this.value = value;}}

子类方法:

@OverridepublicvoidsetValue(Date value){super.setValue(value);}@OverridepublicDategetValue(){returnsuper.getValue();}

为我们的本意是把子类中的泛型也都指定为Date类型,但是由于泛型擦除机制,此时的父类和子类中同名方法的返回类型就变得不相同了,而重写也就变成了重载,泛型擦除就和多态发生冲突。
为了解决这种冲突,JVM引入了泛型的桥接方法。
我们反编译子类的代码

classcom.tao.test.DateInterextendscom.tao.test.Pair<java.util.Date>{com.tao.test.DateInter();Code:0: aload_0  
       1: invokespecial #8// Method com/tao/test/Pair."<init>":()V  4:returnpublicvoidsetValue(java.util.Date);//我们重写的setValue方法  Code:0: aload_0  
       1: aload_1  
       2: invokespecial #16// Method com/tao/test/Pair.setValue:(Ljava/lang/Object;)V  5:returnpublicjava.util.DategetValue();//我们重写的getValue方法  Code:0: aload_0  
       1: invokespecial #23// Method com/tao/test/Pair.getValue:()Ljava/lang/Object;  4: checkcast     #26// class java/util/Date  7: areturn  

  publicjava.lang.ObjectgetValue();//编译时由编译器生成的桥方法  Code:0: aload_0  
       1: invokevirtual #28// Method getValue:()Ljava/util/Date 去调用我们重写的getValue方法;  4: areturn  

  publicvoidsetValue(java.lang.Object);//编译时由编译器生成的桥方法  Code:0: aload_0  
       1: aload_1  
       2: checkcast     #26// class java/util/Date  5: invokevirtual #30// Method setValue:(Ljava/util/Date; 去调用我们重写的setValue方法)V  8:return}

本来我们在子类中知识重写了两个方法,但是从反编译的结果来看,实质上是生成了四个方法,其中两个就是编译器为了解决冲突生成的桥接方法。而打在我们自己定义的setvalue和getValue方法上面的@Override只不过是假象。而桥方法的内部实现,就只是去调用我们自己重写的那两个方法。

标签: java jvm 开发语言

本文转载自: https://blog.csdn.net/dghehe/article/details/129966745
版权归原作者 东莞呵呵 所有, 如有侵权,请联系我们删除。

“Java全栈知识(5)泛型机制详解”的评论:

还没有评论