Javase | 泛型

泛型程序设计

  • 泛型程序设计(Generic Programming)是一种编程范式,它允许在类、接口或方法的定义中使用类型参数,使得类、接口或方法可以在多种数据类型上进行操作 (即同一模板,可填入不同的类型),提高了代码的重用性和类型安全性。
  • 泛型程序设计的目标是编写通用的代码,可以在不同的类型上进行操作 (即可赋予不同的类型),而无需为每种数据类型编写特定的代码】 (一份代码,可填入不同的类型)。通过泛型,可以将代码从具体的数据类型中解耦,使得代码更加通用、灵活和可重用。
  • Java中泛型通过使用类型参数(Type Parameters)来实现,它允许在类、接口或方法的声明中使用一个或多个类型参数。类型参数可以用于指定泛型类、接口或方法中的具体类型,使得类、接口和方法可以以一种通用的方式处理不同的数据类型。

泛型的概念

  • Java中泛型是允许在类、接口和方法的定义类型参数( Type Parameters ),以实现在多种数据类型上进行通用操作的能力 (一份代码,可以填入不同的数据类型,得到不同的效果) 。
  • 通过使用泛型,可以编写一次代码,在多个数据类型上使用 ( 可填入不同的数据类型),而不需要为每个类型编写特定的代码。
  • 泛型的原理:在类、接口、方法的声明中使用类型参数。类型参数可用于指定泛型类、接口或方法中使用的具体类型。

类型参数

  • 类型参数在泛型中可看做占位符,类型参数一般用在泛型类、接口或方法中。

  • 类型参数语法形式为:<T> 或 T

  • 类型参数使用①一个尖括号 <> 和 单个大写字母 或 ②单个大写字母来声明,并放置在类名、接口名、方法名的后面。如class Student<T> 、 class Student<E> 、T age 、T name等。

  • 允许在泛型类、接口或方法的定义中使用占位符来表示一个未知且可自定义的类型。

  • 类型参数可让我们在使用泛型代码时自定义泛型类、接口或方法中的具体的类型,实例化对象时指定要操作的是具体类型。如实例化对象后,类型参数就确定了,属性的类型、方法的返回值等内容的类型也确定了。

  • 例子如:

    看泛型类、接口或泛型方法的例子,泛型参数用在其中。

泛型类

  • 泛型类是一种可以在实例化时指定 类型/数据类型 的类。通过添加尖括号 <> 和类型参数 T 来创建泛型类。

  • 泛型类能增加代码的复用性和灵活性,让我们在创建类时不需要指定具体的类型,而是实例化时根据需要来指定类型。这样不用为每种类型都编写一个新的类,从而创建一个具体类型为T的对象。

    扫描二维码关注公众号,回复: 16723136 查看本文章

    例子如:

    public class Student<T,U> {
           
            //T U为类型参数
    
        private T name; //使用类型参数T赋予name变量的数据类型
        private U age;  //使用类型参数U赋予age变量的数据类型
    
        public T getName() {
           
            //使用类型参数T决定方法的返回值类型
            return name;
        }
    
        public void setName(T name) {
           
            //决定方法参数的数据类型
            this.name = name;
        }
    
        public U getAge() {
           
           //使用类型参数U决定方法的返回值类型
            return age;
        }
    
        public void setAge(U age) {
           
           
            this.age = age;
        }
    
        public static void main(String[] args) {
           
           
            // T:String类型  U:Integer类型
            Student<String,Integer> stu1 = new Student<String,Integer>();
            stu1.setName("张三");
            stu1.setAge(20);
            //张三:String类型  20:Integer类型
            System.out.println(stu1.getName()+" "+stu1.getAge());
    
            // T:String类型  U:String类型
            Student<String,String> stu2 = new Student<String,String>();
            stu2.setName("李四");
            stu2.setAge("21");
            //张三:String类型  20:String类型
            System.out.println(stu2.getName()+" "+stu2.getAge());
        }
    }
    

泛型接口

  • 泛型接口是一种可以在实现接口时指定类型的接口。通过添加尖括号 <> 和类型参数 T 来创建泛型接口。

  • 泛型类能增加代码的复用性和灵活性,让我们在创建接口时不需要指定具体的类型,而是在实现接口时根据需要来指定类型。这样不需要为每一个类型编写一个接口。

  • 例子如:

    定义一个泛型接口:

    public interface Animal_Interface<T,U> {
           
            //两个类型参数
        T run(); //类型参数决定该方法的返回值类型
        U eat();//类型参数决定该方法的返回值类型
    }
    

    实现泛型接口:

    //实现泛型接口
    public class Cat implements Animal_Interface<String, String> {
           
            //自定义类型参数的类型
    
        @Override
        public String run() {
           
            //确定了返回值类型
            return "";
        }
    
        @Override
        public String eat() {
           
           
            return "";
        }
    }
    

泛型方法

  • 泛型方法是一种在方法声明题中使用类型参数的方法。通过添加尖括号 <> 和类型参数 T 来创建泛型方法。

  • 在方法体中,可以使用这些类型参数作为方法的参数列表、返回值类型或局部变量类型等。

    例子如:

    class Eample {
           
           
        public static <T> void printList(T[] elements) {
           
             // T:类型参数
            for (T element : elements) {
           
            //打印数组信息
                System.out.println(element);
            }
        }
    
        public static void main(String[] args) {
           
           
            Integer[] intArray = {
           
            1, 2, 3, 4, 5 }; //Integer类型
            String[] strArray = {
           
            "Hello", "World" }; //String类型
    
            // 使用泛型方法打印 “整数数组”
            Eample.<Integer>printList(intArray);
            // 使用泛型方法打印 “字符串数组“
            Eample.<String>printList(strArray);
        }
    }
    

泛型通配符:

  • 泛型通配符是一种特殊的类型参数,是对Java泛型的一种拓展。
    (但一般不泛型通配符与类型参数混为一谈)
  • 当类型参数被固定时,泛型通配符任能表示任意类型。
  • 泛型通配符包括 ①“未限定通配符” 、 ②“上界通配符”、 ③“下界通配符”
  • 常用未限定通配符,并搭配 “上界通配符” 和 “下界通配符”使用。

未限定通配符

  • ? 表示未限定通配符,语法形式为:<?>

  • 未限定通配符 ? 可表示任意类型。

    例子如:

     //在方法参数中用"未限定通配符"
     public void print(List<?> list) {
           
           
    }
    
     //实例化集合(对象)中用"未限定通配符"
    List<?> list = new ArrayList<>();
    
    //“类型参数”搭配“未限定通配符”使用
    public class MyObject<T> {
           
            //泛型类
        T att;
        // <?> : 未限定通配符,可表示任意类型
        public void print(MyObject<?> object) {
           
            //泛型方法,其中用了未限定通配符
            System.out.println(object.att);
        }
    }
    
     class Test {
           
           
        public static void main(String[] args) {
           
           
    
            //实例化泛型类,其中的泛型参数的类型固定
            //创建一个具体类型为:Integer 的MyObject对象
            MyObject<Integer> obj1 = new MyObject<Integer>();
            //创建一个具体类型为:String 的MyObject对象
            MyObject<String> obj2 = new MyObject<String>();
            obj2.att = "123";
    
            obj1.print(obj2); 
        }
    }
    
  • 未限定通配符? 不能用来当作类型参数的名称。

  • 当类型参数被固定时,未限定通配符 ? 任能表示任意类型。可通过为泛型通配符设置上界和下界来对其进行限制,未限定通配符的边界不能与类型参数设置的边界自相矛盾。

  • 当泛型类被实例化后,此时类型参数也被固定了,导致方法中类型也被固定,如果传入方法中的参数不符合类型要求,会出现 “类型不匹配”报错。 可通过指定未限定通配符 ? 来解决此类问题。

  • 可对未限定通配符设置“上界”和“下界”,来限制泛型通配符的范围。

上界通配符

  • 上界通配符的关键字是 extends ,语法形式为:<T extends 类型> 或 <? extends 类型>

  • 上界通配符用于指定类型参数 (T) 或 未限定通配符(?) 必须是其 “指定类型” 或是其 “子类”。
    (往上已经到顶了,不能再往上了,为上界,自然只能往下走,所以是其指定类型或其子类)

    例子如:

     //泛型类
    public class Teacher<T extends Number> {
           
            //为类型参数指定"上界通配符"
    
        List<? extends Number> list = new ArrayList<>();
    
        //为 “未定义通配符” 指定 “上界通配符”
        public void print(Teacher<? extends Integer> teacher) {
           
           
    
            //创建集合中为 “未定义通配符”指定 “上界通配符”
            List<? extends Number> list = new ArrayList<>();
        }
    
        public static void main(String[] args) {
           
           
            List<? extends Number> list = new ArrayList<>();
        }
    }
    

下界通配符

  • 上界通配符的关键字是 super,语法形式为: <? super 类型>

  • ps : 通常情况下不为类型参数 (T) 设置下界。

  • 下界通配符用于指定 未限定通配符(?) 必须是其 “指定类型” 或是其 “父类”。
    (往下已经到底了,不能再往下了,为下界,自然只能往上走,所以是其指定类型或其父类)

    例子如:

    //泛型类
    public class Teacher<T> {
           
            //一般不为类型参数设置下界
    
        List<? super Number> list = new ArrayList<>();
    
        //为 “未定义通配符” 指定 “下界通配符”
        public void print(Teacher<? super Integer> teacher) {
           
           
    
            //创建集合中为 “未定义通配符”指定 “下界通配符”
            List<? super Number> list = new ArrayList<>();
        }
    
        public static void main(String[] args) {
           
           
            List<? super Number> list = new ArrayList<>();
        }
    }
    

“类型不匹配” 报错

  • 当泛型类被实例化后,类型参数被固定,导致该类中的所有类型都被固定。如:方法中的参数也被固定了类型,如果想传入其他类型的参数就会报“类型不匹配”。可通过设置 未限定通配符? 来解决“类型不匹配”。

    例如:(“类型不匹配”报错)

    public class MyObject<T> {
           
           
        T att;
    
        //泛型方法,参数为一个具体类型为T的MyObject对象
        public void print(MyObject<T> object) {
           
             //需要的参数为: 具体类型为T的MyObject对象
            System.out.println(object.att);
        }
    }
    
     class Test {
           
           
        public static void main(String[] args) {
           
           
    
            //实例化泛型类,其中的泛型参数的类型固定
            MyObject<Integer> obj1 = new MyObject<Integer>();
            MyObject<String> obj2 = new MyObject<String>();
            obj2.att = "123";
    
            /*
             调用obj1对象的print()方法,参数是obj2对象引用,该句代码会有 “类型不匹配”报错,可用泛型通          配符 ? 来解决这个问题。
    
             原因:
             当实例化obj1对象时完成时,此时类型参数已固定为Integer,obj1对象中的print()方法变为:
             public void print(MyObject<Integer> object) {  //该方法需要传入的参数为一个具体类          型为 Integer 的MyObject对象
             但实际传入的是 :  一个具体类型为 String 的MyObject对象。
             自然会有类型冲突的报错。可用泛型通配符 ?来解决这个问题。
             */
            obj1.print(obj2); //会有“类型不匹配”报错
        }
    }
    

未限定通配符 解决 “类型不匹配”错误

  • 通过设置 未限定通配符 ?,在类型参数被固定后,未限定通配符 ?任能表示任意类型来解决“类型不匹配”

    例子如:

    //通过“泛型通配符 ? ”解决 “类型不匹配”的报错
    public class MyObject<T> {
           
           
        T att;
    
           /*
           此处如果用类型参数,当实例化对象后,类型参数被固定,会导致该方法只能接收特定类型的对象作为参    	    数,不太符合开发需要
           而用了泛型通配符之后,类型参数被固定后,println()方法可以以任何具体类型的对象作为参数
    
           ?表示任意类型 当类型参数被固定时,print()方法任然能接收任意类型对象作为参数
           */
        public void print(MyObject<?> object) {
           
           
            System.out.println(object.att);
        }
    }
    
     class Test {
           
           
        public static void main(String[] args) {
           
           
    
            //实例化泛型类,其中的泛型参数的类型固定
            MyObject<Integer> obj1 = new MyObject<Integer>();
            MyObject<String> obj2 = new MyObject<String>();
            obj2.att = "123";
    
            /*
             * MyObject的print()方法参数设置了泛型通配符,这时它可不受类型参数的固定而依然能接收任意类	       * 型对象作为参数
             * ? 表示任意类型
             */
            obj1.print(obj2); //这里不再会报错。
        }
    }
    

泛型的注意事项

  1. 泛型的类型参数只存在于编译时:泛型在编译后会进行类型擦除,即泛型的类型参数会被擦除到其上界类型 (或object类型) ,因此在运行时无法获取类型参数的具体信息。

  2. 泛型数组的创建是非法的 : 不能直接创建带有类型参数的数组。例如:

 List<Integer>[] listArray = new List<Integer>[10]; // 非法
  1. 无法使用基本数据类型作为泛型类型参数:泛型类型参数只能是引用类型,不能使用基本数据类型。如果需要使用基本数据类型,可以使用其包装类。

  2. 泛型类型参数不能使用类型运算符:不能使用 instanceof 运算符来判断泛型类型参数的具体类型,例如
    if (obj instanceof List) { ... } 是非法的。可以通过其他方式来判断,如使用辅助方法或通过类型转换。

  3. 类型参数不能是静态变量或静态方法的类型:在泛型类中,类型参数不能是静态变量的类型,也不能作为静态方法的参数或返回值类型。

  4. 泛型类型参数不能使用原始类型:在泛型类或泛型方法中,不能使用泛型类型参数的原始类型,例如不能使用 T 作为 instanceof 运算符的操作数。

  5. 类型擦除可能导致运行时的类型不安全:由于类型擦除的存在,泛型在某些情况下可能导致运行时的类型不安全。在使用泛型时需要注意类型转换和类型检查,以避免可能的类型错误。

  6. 不能使用一个本地类型(如int、float)来替换泛型。

  7. 运行时类型检查,不同类型的泛型类是等价的。

  8. 不能继承Exception类,不能作为异常被抛出。

  9. 不能使用泛型构造对象。

  10. 在static方法中不能使用泛型,泛型变量也不可用static关键字来修饰

猜你喜欢

转载自blog.csdn.net/m0_70720417/article/details/132506921