容器-泛型总结

容器-泛型

  1. 泛型的定义:

    **泛型“理解”**为数据类型的一个占位符(类似:形式参数),告诉编译器,在调用泛型时必须传入实际类型。这种参数类型可以用在类、接口、方法中、分别被成为泛型类、泛型接口、泛型方法。

  2. 泛型字符
    在这里插入图片描述

  3. 泛型类

    • 建立一个泛型类Generic

      /泛型类,定义泛型:<T>是用于普通类
      public class Generic<T> {
              
              
          private T flag;//如果这里没用到泛型,我们要先要定义一个  Object object = new Student();
          // 再用object instanceof Student,判断是否是String、integer这些引用类型
          // 如果是,当我们要调用这个属性的时候,就要把他强转成String类型或者是integer类型,,否则就会报类型转换异常
          //有了泛型之后,这些就变得简单了,也不要去定义Object,也不用去强转,直接在属性面前加一个T的泛型标记
          // 通通帮你搞定,他会再你调用的时候帮你直接转成String这些引用类型。
      
          public void setFlag(T flag){
              
              
              this.flag=flag;
          }
      
          public T getFlag(){
              
              
              return this.flag;
          }
      
      }
      
      
    • 再用一个测试类 Test

      //当我们用泛型的时候,才能定义泛型的类型,否则我们是不知道的
      public class Test {
              
              
          public static void main(String[] args) {
              
              
              Generic<String> generic=new Generic<>();
              //如果这里 Generic<String> 没有给定时String类型,generic.setFlag();的时候是Object
              generic.setFlag("admin");
              String flag=generic.getFlag();//如果这里不是泛型来调用的话,要先把他搞成Object,再
              // 用instanceof去判断,是否是String类型,是的话调用要进行强转成String类型,
              //但是这里是泛型的话,直接就可以调用,因为它帮你自动转换成String类型
              System.out.println(flag);
      
      
              Generic<Integer> generic1=new Generic<>();
              generic1.setFlag(100);
              Integer flag1=generic1.getFlag();
              System.out.println(flag1);
      
          }
      }
      
      
  4. 泛型接口

    • 先建立一个Igeneric的泛型接口。

      //泛型接口,在接口时使用泛型类型
      public interface Igeneric<T> {
              
              
          T getName(T name);//当接口使用泛型类型时,方法的类型也可以使用泛型,方法的参数类型也可以使用泛型。
      
      }
      
    • 再定义一个IgenericImpl的接口实现类

      //接口实现类,因为Igeneric是泛型的接口,所以要使用具体的泛型Igeneric<String>
      public class IgenericImpl implements Igeneric<String> {
              
              
      
          @Override
          public String getName(String name) {
              
              //为什么方法类型和方法参数都是String类型,
                                              // 因为你上面实现的是具体的接口时是String类型
              return name;
          }
      }
      
    • 再来个测试类Test2

      public class Test2 {
              
              
          public static void main(String[] args) {
              
              
              //对于实例化对象的引用,现在有两种方法,
              //1.接口实现类的类型来定义
              IgenericImpl igeneric=new IgenericImpl();
              String name = igeneric.getName("oldru");//getName();是自动转成String类型的,因为IgenericImpl类的重写方法都是String类型
              System.out.println(name);
      
              //2.接口类型来定义
              Igeneric<String> igeneric1=new IgenericImpl();//这里要特别注意,因为Igeneric是泛型<T>,
              // 还没给具体的String类型,你这里就要加<String>具体的类型,否则你的getName();还是Object类型
              String name1=igeneric1.getName("bjxt");
              System.out.println(name1);
          }
      }
      
  5. 泛型方法:泛型不在是通过定义在泛型类上,在去使用泛型方法,而是直接把泛型定义装在方法上,参数类型可以有多个,用逗号隔开。如:<K,V>.

    **这样做的好处是:**调用泛型方法时,不在需要像泛型类上去告诉编译器是什么类型,编译器会自动帮你转换。

  6. 泛型的静态方法:

    • 先建立一个MethodGeneric类的泛型方法,这个是个非静态的方法

      public class MethodGeneric {
              
              
      
          //之前的是先把泛型定义在类上,然后再通过类上的泛型,去用在方法上的。
          //现在不是,直接把泛型定义装在方法上
          //泛型用在方法上,注意<T>要写在返回值类型的前面  //这是没有返回值的非静态方法
          public <T> void setName(T name){
              
              
              System.out.println(name);
          }
      
          //定义有返回值的非静态方法
          public <T> T getname(T name){
              
              
              return name;
          }
      }
      
      
    • 在定义一个测试类Test3

      public class Test3 {
              
              
          public static void main(String[] args) {
              
              
              //测试的是没有返回值的非静态方法
              MethodGeneric methodGeneric=new MethodGeneric();
              //这里的MethodGeneric,不用再去强调具体是什么类型了,因为你在调用的时候,编译器会自动帮你转换
              methodGeneric.setName("oldlu");
              methodGeneric.setName(12312313);
      
      
              //测试的是有返回值的非静态方法
              MethodGeneric methodGeneric2=new MethodGeneric();
              String name=methodGeneric2.getname("bjsxt");//getname();的类型是根据我们这个name属性的String类型推断出来的
              Integer name1=methodGeneric2.getname(123);//getname();的类型是根据我们这个name1属性的Integer类型推断出来的
              System.out.println(name1);                  //所以,才能正常打印
              System.out.println(name);
      
          }
      }
      
      
  7. 泛型的静态方法:

    • 先建立一个 MethodGeneric类的泛型方法,这是一个静态的方法。

      public class MethodGeneric {
              
              
           //定义的是没有返回值的静态方法
          public static <T> void setFlag(T flag){
              
              
              System.out.println(flag);
          }
      
          //定义的是有返回值的静态方法
          public static <T> T getFlag(T flag){
              
              
              return flag;
          }
      }
      
      
    • 再建立一个测试类Test4

      public class Test4 {
              
              
          public static void main(String[] args) {
              
              
              //调用静态方法是不用实例化对象的引用的,直接调用
              //静态方法没有返回值的调用
              MethodGeneric.setFlag("oldlu");
              MethodGeneric.setFlag(123123);
      
              //静态方法有返回值的调用
              String flag=MethodGeneric.getFlag("bjsxt");
              System.out.println(flag);
              Integer flag1=MethodGeneric.getFlag(123132);
              System.out.println(flag1);
          }
      }
      
  8. 泛型方法的非静态方法和静态方法的区别:

    非静态方法可以通过两种方式获得泛型,第一从泛型类中,根据泛型类提供的泛型给泛型方法使用,第二 就是直接在泛型方法直接定义泛型。

    静态方法则只能通过在泛型方法上直接定义泛型,不能直接从泛型类中获得。

  9. 泛型方法中的参数是可变参数:

    • 先建立一个MethodGeneric类,去写一个泛型方法的可变参数。

      public class MethodGeneric {
              
              
          //泛型方法的参数是可变参数
          public <T> void method(T... args){
              
              
              for (T t:args){
              
              
                  System.out.println(t);
              }
          }
      
      }
      
    • 在建立一个Test5

      public class Test5 {
              
              
          public static void main(String[] args) {
              
              
              MethodGeneric methodGeneric= new MethodGeneric();
              String[] arr=new String[]{
              
              "a","b","c"};
              Integer[] arr2=new Integer[]{
              
              1,2,3};
              methodGeneric.method(arr);
              methodGeneric.method(arr2);
      
          }
      }
      
      
  10. 泛型的通配符:“?”表示通配符,用于代替具体的类型。

    • 先定义一个泛型类,里面有反省过的get、set的方法。

      //泛型类,定义泛型:<T>是用于普通类
      public class Generic<T> {
              
              
          private T flag;
          
          public void setFlag(T flag){
              
              
              this.flag=flag;
          }
          
          public T getFlag(){
              
              
              return this.flag;
          }
      
      }
      
      
    • 再建立一个ShowMsg类,定义一个专门输出Generic类中flag的值的方法。

      //Generic<?>  ?就是泛型的通配符
      public class ShowMsg {
              
              
          //这个方法的作用:就是专门输出Generic类中flag的值
          public void showFlag(Generic<?> generic){
              
              
              //Generic<?> 这里如果不是?通配符,你就要定义一个具体的泛型,比<String>泛型
              //但是你如果是Generic<String>,他调用时就只能限定了它只能是<String>才能调用
              //如果是别的,比如<Integer>类型,它就调用不了了
              //所以为了解决这个问题,我们就使用的通配符?,这样调用的时候就什么泛型都可以调用了
              System.out.println(generic.getFlag());
          }
      }
      
      
    • 在建立一个测试类Test6,测试什么类型都可以打印,就是因为有?通配符。

      public class Test6 {
              
              
          public static void main(String[] args) {
              
              
              ShowMsg showMsg= new ShowMsg();
              Generic<Integer> generic=new Generic<>();
              //这里的Generic<Integer>本来是要和ShowMsg类的Generic<Integer> 的泛型一样的才能调用
              //但是,因为下面还有不同的泛型要调用,就会冲突了,所以把ShowMsg类的Generic<?>搞成统配符,这样就谁都可以调用了
              //还有注意一个问题Number是Integer的父类,那你的ShowMsg类的Generic<Number> ,是不是以为Number和Integer泛型都可以调用了,
              //答:不是,因为这里的继承关系是没法在这里使用的
              generic.setFlag(20);
              showMsg.showFlag(generic);
      
      
              Generic<Number> generic1=new Generic<>();
              generic1.setFlag(50);
              showMsg.showFlag(generic1);
      
              Generic<String> generic2=new Generic<>();
              generic2.setFlag("oldlu");
              showMsg.showFlag(generic2);
          }
      }
      
      
  11. 泛型的统配符的上限限定:通配符的类型是T类以及T类的子类后者是T接口以及T接口的子接口。

    • 先定义一个泛型类,里面有反省过的get、set的方法。

      //泛型类,定义泛型:<T>是用于普通类
      public class Generic<T> {
              
              
          private T flag;
          
          public void setFlag(T flag){
              
              
              this.flag=flag;
          }
          
          public T getFlag(){
              
              
              return this.flag;
          }
      
      }
      
    • 再建立一个ShowMsg 类,是泛型方法的泛型通配符被上限限定。

      //Generic<?>  ?就是泛型的通配符
      public class ShowMsg {
              
              
          //这个方法的作用:就是专门输出Generic类中flag的值
          public void showFlag(Generic<? extends Number> generic){
              
              
              //泛型通配符的上限限定,说明了泛型的类型继承了Number,也就是缩小了范围
              //他的范围只能是Number类型,或者Number类型的子类Integer
               System.out.println(generic.getFlag());
          }
      }
      
    • 再建立一个测试类Test6

      public class Test6 {
              
              
          public static void main(String[] args) {
              
              
              //由于泛型统配符的方法被上限限定了,范围只能在Numeber类型和Numeber的子类Interger类型,只有才这两个类型才能调用,其他类型则无法调用
              ShowMsg showMsg= new ShowMsg();
              Generic<Integer> generic=new Generic<>();
              generic.setFlag(20);
              showMsg.showFlag(generic);
      
      
              Generic<Number> generic1=new Generic<>();
              generic1.setFlag(50);
              showMsg.showFlag(generic1);
      
              /*Generic<String> generic2=new Generic<>();
              generic2.setFlag("oldlu");
              showMsg.showFlag(generic2);//这里的泛型统配符的被上限限定了,导致这里的String类型就不能使用*/
          }
      }
      
      
  12. **泛型通配符的下限限定:**通配符的类型是T类以及T类的父类后者是T接口以及T接口的父接口。

    注意:该方法适用泛型类。

    • 先定义一个泛型类,里面有反省过的get、set的方法。

      //泛型类,定义泛型:<T>是用于普通类
      public class Generic<T> {
              
              
          private T flag;
          
          public void setFlag(T flag){
              
              
              this.flag=flag;
          }
          
          public T getFlag(){
              
              
              return this.flag;
          }
      
      }
      
    • 建立一个ShowMsg 类,泛型方法的泛型通配符被下限限定。

      //Generic<?>  ?就是泛型的通配符
      public class ShowMsg {
              
              
          //这个方法的作用:就是专门输出Generic类中flag的值
          public void showFlag(Generic<? super Integer> generic){
              
              
              //Generic<? super Integer> generic 泛型统配符的下限限定
              //限定的是当前类型和当前类型的父类才能使用
              //这样Integer类型能使用,Integer类型的父类Number也能使用
              
                System.out.println(generic.getFlag())
          }
      }
      
    • 再建立一个测试类Test6

      public class Test6 {
              
              
          public static void main(String[] args) {
              
              
              //由于泛型统配符的方法被上限限定了,范围只能在Numeber类型和Numeber的子类Interger类型,只有才这两个类型才能调用,其他类型则无法调用
              ShowMsg showMsg= new ShowMsg();
              Generic<Integer> generic=new Generic<>();
              generic.setFlag(20);
              showMsg.showFlag(generic);
      
      
              Generic<Number> generic1=new Generic<>();
              generic1.setFlag(50);
              showMsg.showFlag(generic1);
                  }
      }
      
  13. 泛型的总结:

    泛型主要用于编译阶段,编译后生成的字节码class文件包含泛型中的类型信息。**类型编译参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。**因此,使用泛型时,有如下几种错误:

    • 基本类型不能用于类型:

      如:Test t; 错的,们可以使用对应的包装类,Test t;

    • 不能通过类型参数创建对象:

      T elm = new T(); 运行时类型参数T会被替换成Object,无法创建T类型的对象,容易引起误解,所以在java中支持。

猜你喜欢

转载自blog.csdn.net/Xun_independent/article/details/114635545