• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
ZhengLord
博客园    首页    新随笔    联系   管理    订阅  订阅
泛型相关介绍

Java泛型

一、泛型引入

编写一段程序,在ArrayList中,添加3个Dog对象,Dog对象含有name和age属性,并输出name和age

  1. 不使用泛型解决

    package com.hspedu.generic;
    
    import java.util.ArrayList;
    @SuppressWarnings({"all"})
    public class Generic01 {
        public static void main(String[] args) {
    
            //使用传统的方法来解决
            ArrayList arrayList = new ArrayList();
            arrayList.add(new Dog("旺财", 10));
            arrayList.add(new Dog("发财", 1));
            arrayList.add(new Dog("小黄", 5));
    
            //假如我们的程序员,不小心,添加了一只猫
            arrayList.add(new Cat("招财猫", 8));//由于定义ArrayList时并没有指定类(默认是Object类型),所以这里加入Cat类型并不会报错,但到后面转型成Dog类型时会报运行错误
            //遍历
            for (Object o : arrayList) {//这里o的类型必须是Object不能指定成Dog,因为在定义的时候没有指定类型默认为Object;需要强制转型才能转成Dog类型
                //向下转型Object ->Dog
                Dog dog = (Dog) o;
                System.out.println(dog.getName() + "-" + dog.getAge());
            }
    
        }
    }
    /*
    请编写程序,在ArrayList 中,添加3个Dog对象
    Dog对象含有name 和 age, 并输出name 和 age (要求使用getXxx())
     */
    class Dog {
        private String name;
        private int age;
        public Dog(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    class Cat { //Cat类
        private String name;
        private int age;
        public Cat(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
  2. 使用泛型

    package com.hspedu.generic.improve;
    
    import java.util.ArrayList;
    
    @SuppressWarnings({"all"})
    public class Generic02 {
        public static void main(String[] args) {
    
            //使用传统的方法来解决===> 使用泛型
            //1. 当我们 ArrayList<Dog> 表示存放到 ArrayList 集合中的元素是Dog类型 
            //2. 如果编译器发现添加的类型,不满足要求,就会报错
            //3. 在遍历的时候,可以直接取出 Dog 类型而不是 Object
            //4. public class ArrayList<E> {} E称为泛型,那么 Dog->E
            ArrayList<Dog> arrayList = new ArrayList<Dog>();
            arrayList.add(new Dog("旺财", 10));
            arrayList.add(new Dog("发财", 1));
            arrayList.add(new Dog("小黄", 5));
            //假如我们的程序员,不小心,添加了一只猫
            //arrayList.add(new Cat("招财猫", 8));这里直接会报编译错误
            System.out.println("===使用泛型====");
            for (Dog dog : arrayList) {
                System.out.println(dog.getName() + "-" + dog.getAge());
            }
    
    
        }
    }
    
    /*
    1.请编写程序,在ArrayList 中,添加3个Dog对象
    2.Dog对象含有name 和 age, 并输出name 和 age (要求使用getXxx())
    3.老师使用泛型来完成代码
     */
    class Dog {
        private String name;
        private int age;
        public Dog(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    class Cat { //Cat类
        private String name;
        private int age;
        public Cat(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    
  3. 总结

    • 不使用泛型

      • 加入-Dog—>取出-Object—>转换成Dog

      • 不能对加入到集合中的数据类型进行约束(不安全)

      • 遍历时,需要进行类型转换,如果集合中的数据量较大,对效率有影响

    • 使用泛型后

      • Dog—>Dog—>Dog//加入和取出不需要类型转换

      • 编译时,检查添加元素的类型,提高了安全性

      • 减少了类型转换的次数,提高效率

二、基本介绍

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

2.1语法

  • 泛型的声明

    interface 接口名 {} 和 class 类名 <K,V>{}

    注:1)T,K,V不代表值,而是表示类型

    2)任意字母都可以。常用T(Type)表示

  • 泛型的实例化

    要在类名的后面指定类型参数的值(也即类型)。如:

    List<String> strList =new ArrayList<String>();
    
  • 泛型的注意事项和使用细节

    • 泛型 只能是引用类型

    • 在给泛型指定具体类型后,可以传入该类型或者其子类型

    • 泛型使用形式

      List<Integer> list =new ArrayList<>();//后面的尖括号中内容可以省略
      //如果都不写默认为Object类型
      
    • 示例

      package com.hspedu.generic;
      
      import java.util.ArrayList;
      import java.util.List;
      
      @SuppressWarnings({"all"})
      public class GenericDetail {
          public static void main(String[] args) {
              //1.给泛型指向数据类型是,要求是引用类型,不能是基本数据类型
              List<Integer> list = new ArrayList<Integer>(); //OK
              //List<int> list2 = new ArrayList<int>();//错误
      
              //2. 说明
              //因为 E 指定了 A 类型, 构造器传入了 new A()
              //在给泛型指定具体类型后,可以传入该类型或者其子类类型
              Pig<A> aPig = new Pig<A>(new A());
              aPig.f();
              Pig<A> aPig2 = new Pig<A>(new B());
              aPig2.f();
              
              ArrayList<A> arrayList1 = new ArrayList<>();
              arrayList1.add(new A());
              arrayList1.add(new B());
              //arrayList1.add("sss");因为指定了A类为类型,所以字符串是传不进去的
      
              //3. 泛型的使用形式
              ArrayList<Integer> list1 = new ArrayList<Integer>();
              List<Integer> list2 = new ArrayList<Integer>();
              //在实际开发中,我们往往简写
              //编译器会进行类型推断, 老师推荐使用下面写法
              ArrayList<Integer> list3 = new ArrayList<>();
              List<Integer> list4 = new ArrayList<>();
              ArrayList<Pig> pigs = new ArrayList<>();
      
              //4. 如果是这样写 泛型默认是 Object
              ArrayList arrayList = new ArrayList();//等价 ArrayList<Object> arrayList = new ArrayList<Object>();
              
              Tiger tiger = new Tiger();
              /*
      
                  class Tiger {//类
                      Object e;
      
                      public Tiger() {}
      
                      public Tiger(Object e) {
                          this.e = e;
                      }
                  }
      
               */
      
          }
      }
      class Tiger<E> {//类
          E e;
      
          public Tiger() {}
      
          public Tiger(E e) {
              this.e = e;
          }
      }
      
      class A {}
      class B extends A {}
      
      class Pig<E> {//
          E e;
      
          public Pig(E e) {
              this.e = e;
          }
      
          public void f() {
              System.out.println(e.getClass()); //运行类型
          }
      }
      

三、自定义泛型

3.1自定义泛型类

  1. 语法

    class 类名<T,R,...>{//...表示可以有多个泛型

    }

  2. 细节

    • 普通成员(属性、方法)可以使用泛型
    • 使用泛型的数组不能被初始化
    • 静态方法中不能使用类的泛型
    • 泛型类的类型是在创建对象时确定的(因为创建对象时,需要确定类型)
  3. 示例:

    package com.hspedu.customgeneric;
    
    import java.util.Arrays;
    
    @SuppressWarnings({"all"})
    public class CustomGeneric_ {
        public static void main(String[] args) {
    
            //T=Double, R=String, M=Integer
            Tiger<Double,String,Integer> g = new Tiger<>("john");
            g.setT(10.9); //OK
            //g.setT("yy"); //错误,类型不对
            System.out.println(g);
            Tiger g2 = new Tiger("john~~");//OK T=Object R=Object M=Object
            g2.setT("yy"); //OK ,因为 T=Object "yy"=String 是Object子类
            System.out.println("g2=" + g2);
    
        }
    }
    
    //1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类
    //2, T, R, M 泛型的标识符, 一般是单个大写字母
    //3. 泛型标识符可以有多个.
    //4. 普通成员可以使用泛型 (属性、方法)
    //5. 使用泛型的数组,不能初始化
    //6. 静态方法中不能使用类的泛型
    class Tiger<T, R, M> {
        String name;
        R r; //属性使用到泛型
        M m;
        T t;
        //因为数组在new 不能确定T的类型,就无法在内存开空间
        T[] ts;
    
        public Tiger(String name) {
            this.name = name;
        }
    
        public Tiger(R r, M m, T t) {//构造器使用泛型
    
            this.r = r;
            this.m = m;
            this.t = t;
        }
    
        public Tiger(String name, R r, M m, T t) {//构造器使用泛型
            this.name = name;
            this.r = r;
            this.m = m;
            this.t = t;
        }
    
        //因为静态是和类相关的,在类加载时,对象还没有创建
        //所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
    //    static R r2;
    //    public static void m1(M m) {
    //
    //    }
    
        //方法使用泛型
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public R getR() {
            return r;
        }
    
        public void setR(R r) {//方法使用到泛型
            this.r = r;
        }
    
        public M getM() {//返回类型可以使用泛型.
            return m;
        }
    
        public void setM(M m) {
            this.m = m;
        }
    
        public T getT() {
            return t;
        }
    
        public void setT(T t) {
            this.t = t;
        }
    
        @Override
        public String toString() {
            return "Tiger{" +
                    "name='" + name + '\'' +
                    ", r=" + r +
                    ", m=" + m +
                    ", t=" + t +
                    ", ts=" + Arrays.toString(ts) +
                    '}';
        }
    }
    

3.2自定义接口泛型

  1. 语法

    interface 接口名<T,R,...>{

    }

  2. 细节

    • 接口中,静态成员也不能使用泛型(接口中属性默认为public static final)
    • 泛型接口的类型,在继承接口或者实现接口时确定
  3. 示例:

    package com.hspedu.customgeneric;
    
    public class CustomInterfaceGeneric {
        public static void main(String[] args) {
    
        }
    }
    
    /**
     *  泛型接口使用的说明
     *  1. 接口中,静态成员也不能使用泛型
     *  2. 泛型接口的类型, 在继承接口或者实现接口时确定
     *  3. 没有指定类型,默认为Object
     */
    
    //在继承接口 指定泛型接口的类型
    interface IA extends IUsb<String, Double> {
    
    }
    //当我们去实现IA接口时,因为IA在继承IUsu 接口时,指定了U 为String R为Double
    //,在实现IUsu接口的方法时,使用String替换U, 是Double替换R
    class AA implements IA {
    
        @Override
        public Double get(String s) {
            return null;
        }
        @Override
        public void hi(Double aDouble) {
    
        }
        @Override
        public void run(Double r1, Double r2, String u1, String u2) {
    
        }
    }
    
    //实现接口时,直接指定泛型接口的类型
    //给U 指定Integer 给 R 指定了 Float
    //所以,当我们实现IUsb方法时,会使用Integer替换U, 使用Float替换R
    class BB implements IUsb<Integer, Float> {
    
        @Override
        public Float get(Integer integer) {
            return null;
        }
    
        @Override
        public void hi(Float aFloat) {
    
        }
    
        @Override
        public void run(Float r1, Float r2, Integer u1, Integer u2) {
    
        }
    }
    //没有指定类型,默认为Object
    //建议直接写成 IUsb<Object,Object>
    class CC implements IUsb { //等价 class CC implements IUsb<Object,Object> {
        @Override
        public Object get(Object o) {
            return null;
        }
        @Override
        public void hi(Object o) {
        }
        @Override
        public void run(Object r1, Object r2, Object u1, Object u2) {
    
        }
    
    }
    
    interface IUsb<U, R> {
    
        int n = 10;
        //U name; 不能这样使用
    
        //普通方法中,可以使用接口泛型
        R get(U u);
    
        void hi(R r);
    
        void run(R r1, R r2, U u1, U u2);
    
        //在jdk8 中,可以在接口中,使用默认方法, 也是可以使用泛型
        default R method(U u) {
            return null;
        }
    }
    

3.3自定义泛型方法

  1. 语法

    修饰符 <T,R,...>返回类型 方法名(参数列表){

    }

  2. 细节

    • 泛型方法,可以定义在普通类中,也可以定义在泛型类中
    • 当方法被调用时,类型会确定
    • public void eat(E e){},修饰符后面没有<T,R,...>eat 方法不是泛型方法,而是使用了泛型
  3. 示例:

    package com.hspedu.customgeneric;
    
    /**
     * @author 韩顺平
     * @version 1.0
     */
    public class CustomMethodGenericExercise {
        public static void main(String[] args) {
            //T->String, R->Integer, M->Double
            Apple<String, Integer, Double> apple = new Apple<>();
            apple.fly(10);//10 会被自动装箱 Integer10, 输出Integer
            apple.fly(new Dog());//Dog
    
        }
    }
    
    class Apple<T, R, M> {//自定义泛型类
    
        public <E> void fly(E e) {  //泛型方法
            System.out.println(e.getClass().getSimpleName());
        }
    
        //public void eat(U u) {}//错误,因为U没有声明
        public void run(M m) {
        } //ok
    }
    
    class Dog {
    }
    
    

3.4泛型的继承和通配符

  • 泛型不具备继承性

    List<Object> list = new ArrayList<String>();//不对
    
  • :支持任意泛型类型
  • :支持A类以及A类的子类,规定了泛型的上限
  • :支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
  • 示例:

    package com.hspedu;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class GenericExtends {
        public static void main(String[] args) {
    
            Object o = new String("xx");
    
            //泛型没有继承性
            //List<Object> list = new ArrayList<String>();
    
            //举例说明下面三个方法的使用
            List<Object> list1 = new ArrayList<>();
            List<String> list2 = new ArrayList<>();
            List<AA> list3 = new ArrayList<>();
            List<BB> list4 = new ArrayList<>();
            List<CC> list5 = new ArrayList<>();
    
            //如果是 List<?> c ,可以接受任意的泛型类型
            printCollection1(list1);
            printCollection1(list2);
            printCollection1(list3);
            printCollection1(list4);
            printCollection1(list5);
    
            //List<? extends AA> c: 表示 上限,可以接受 AA或者AA子类
    //        printCollection2(list1);//×
    //        printCollection2(list2);//×
            printCollection2(list3);//√
            printCollection2(list4);//√
            printCollection2(list5);//√
    
            //List<? super AA> c: 支持AA类以及AA类的父类,不限于直接父类
            printCollection3(list1);//√
            //printCollection3(list2);//×
            printCollection3(list3);//√
            //printCollection3(list4);//×
            //printCollection3(list5);//×
    
        }
        // ? extends AA 表示 上限,可以接受 AA或者AA子类
        public static void printCollection2(List<? extends AA> c) {
            for (Object object : c) {
                System.out.println(object);
            }
        }
    
        //说明: List<?> 表示 任意的泛型类型都可以接受
        public static void printCollection1(List<?> c) {
            for (Object object : c) { // 通配符,取出时,就是Object
                System.out.println(object);
            }
        }
    
    
        // ? super 子类类名AA:支持AA类以及AA类的父类,不限于直接父类,
        //规定了泛型的下限
        public static void printCollection3(List<? super AA> c) {
            for (Object object : c) {
                System.out.println(object);
            }
        }
    
    }
    
    class AA {
    }
    
    class BB extends AA {
    }
    
    class CC extends BB {
    }
    
posted on 2022-11-23 15:00  ZhengLord  阅读(37)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3