• 一文学懂java泛型


    1.什么是java泛型

    Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

    泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

    假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?

    答案是可以使用 Java 泛型。

    使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。


    2.泛型的快速入门

    代码示例:

    import java.util.ArrayList;
    
    /**
     * java泛型
     */
    public class Generics {
        public static void main(String[] args) {
            // 使用java泛型对数据类型做限制
            // 限制添加的类型必须为Dog
            ArrayList<Dog> arrayList = new ArrayList<Dog>();
            arrayList.add(new Dog("康康",12));
            arrayList.add(new Dog("旺旺",6));
            System.out.println(arrayList);
        }
    }
    
    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;
        }
    
        @Override
        public String toString() {
            return "Dog{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    使用泛型,可以直接对于数据进行遍历,无需进行额外的类型强转,这增大了代码的效率:🎶

    for (Dog dog : arrayList) {
        System.out.println(dog.getName());
        System.out.println(dog.getAge());
    }
    
    • 1
    • 2
    • 3
    • 4

    3.拥有泛型特性的类

    我们来创建一个类:

    该类的一个属性为泛型,可以是任意的数据类型,在编译期间,确定E是什么类型

    // 泛型类
    class Person<E> {
        E s; // 在编译期间,确定E是什么类型
    
        public Person(E s) { // E可以是参数类型
            this.s = s;
        }
    
        public E f() { // E也可以是返回类型
            return s;
        }
    	@Override
        public String toString() {
            return "Person{" +
                    "s=" + s +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    那么我们在创建对象的时候,就可以传入不同的数据类型的数据,这很方便:😋

    例如:

    Person<String> stringPerson = new Person<String>("www");
    System.out.println(stringPerson);
    Person<Integer> integerPerson = new Person<Integer>(521);
    System.out.println(integerPerson);
    -------------------------------
    输出:
    Person{s=www}
    Person{s=521}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.泛型的使用细节

    泛型的<>里面只能是引用类型,不可以为基本数据类型🤪

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

    在实际的开发中,我们一般采用简写的泛型语法(去掉后面<>里面的内容),例如:

    Person<Double> doublePerson = new Person<>(13.14);
    
    • 1

    如果不给泛型指定具体的数据类型,默认为Object类型✨


    5.自定义泛型类

    泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。

    和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

    • 使用泛型的数组,不能初始化
    • 静态成员不能使用泛型,因为在类加载时,对象还没有创建,JVM无法完成其的初始化操作

    接下来,我们看一个自定义泛型类的例子:

    /**
     * 自定义泛型类
     */
    class Tiger<T,R,M> {
        String name;
        R r;
        M m;
        T t;
    
        public Tiger(String name, R r, M m, T t) {
            this.name = name;
            this.r = r;
            this.m = m;
            this.t = t;
        }
    
        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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    6.自定义泛型接口

    • 在接口中,静态成员也不能使用泛型
    • 泛型接口的类型,在继承接口或者实现接口的时候确定
    • 没有指定类型,默认依然为Object类型

    自定义泛型接口的例子:

    /**
     * 自定义泛型接口
     */
    interface IUsb<U, R> {
        R get(U u);
    
        void hi(R r);
    
        void run(R r1, R r2, U u1, U u2);
    
        default R method(U u) {
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这个自定义泛型的接口,如果有接口想要继承他,可以在继承接口时指定泛型接口的类型:

    /**
     * 继承接口时指定泛型接口的类型
     */
    interface IA extends IUsb<String,Double>{
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    现在当我们实现IA接口时,会用StringDouble类型替换我们接口原有的U和R类型:

    例如:

    /**
     * 实现自定义的泛型接口
     */
    class TTT 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) {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    当然,我们也可以在实现接口的同时指定接口的数据类型,例如:

    /**
     * 实现IUsb接口
     */
    class III implements IUsb<Integer,String> {
    
        @Override
        public String get(Integer integer) {
            return null;
        }
    
        @Override
        public void hi(String s) {
    
        }
    
        @Override
        public void run(String r1, String r2, Integer u1, Integer u2) {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    7.自定义泛型方法

    • 泛型方法,可以定义在普通类中,也可以定义在泛型类中
    • 泛型方法在调用时,类型就确定了

    使用代码示例:

    public class AdvancedGenerics {
        public static void main(String[] args) {
            fly("保密",521);
            fly(12,true);
        }
    
        /**
         * 自定义泛型方法
         */
        static public <T, R> void fly(T t, R r) {
            System.out.println(t);
            System.out.println(r);
        }
    }
    ---------------------------
    输出:
    保密
    521
    12
    true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    8.泛型通配符

    泛型不具备继承性

    类型通配符一般是使用 ? 代替具体的类型参数。例如 List 在逻辑上是List,List等所有 List<具体类型实参> 的父类

    /**
     * 任意泛型类型
     */
    public static void printCollection(List<?> c){
        for (Object o : c) {
            System.out.println(o);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    表示该通配符所代表的类型是T类型的子类。
    表示该通配符所代表的类型是T类型的父类。

    /**
     * ? extends 表示上限,如下代表可以接受AA或者AA的子类
     */
    public static void printCollection2(List<? extends AA>c){
        for (AA aa : c) {
            System.out.println(aa);
        }
    }
    
    /**
     * ? super 表示下限,如下表示支持AA类以及AA类的父类,不限于直接父类
     */
    public static void printCollection3(List<? super AA>c) {
        for (Object o : c) {
            System.out.println(o);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    知识给人重量,成就给人光彩,大多数人只是看到了光彩,而不去称量重量。🍳

  • 相关阅读:
    <C++> 模板-上
    【论文笔记】基于强化学习的机械臂自主视觉感知控制方法
    基本算法:二分
    浅析Java设计模式【3.4】——策略
    mock+封装axios+vuex分发
    Angular10.0项目中使用mock.js
    java面试题
    HTB-Antique
    “综合”web项目编写------手把手0基础教学(一)
    HBUILDER vue启动失败
  • 原文地址:https://blog.csdn.net/Gherbirthday0916/article/details/126053058