12.Java 泛型(自定义泛型类、自定义泛型接口、自定义泛型方法、泛型的继承与通配符)

一、泛型引入

1、为什么需要泛型

传统方式存在的问题

  1. 不能对加入到集合中的数据类型进行约束

  2. 遍历时,需要进行类型转换

泛型的理解与好处

  1. 编译时能检查添加元素的类型

  2. 能减少类型转换的次数

2、泛型初体验
(1)说明
  • 这里以 Dog 类为例
java 复制代码
ArrayList<Dog> list = new ArrayList<Dog>();
  1. 不使用泛型:放入集合时,Dog 类型会先转换为 Object 类型,取出时,需要转换为 Dog 类型

  2. 使用泛型:放入集合和取出时,不需要进行类型转换

(2)演示
java 复制代码
package com.my.test;

import java.util.ArrayList;
import java.util.Iterator;

public class GenericTest {
    public static void main(String[] args) {
        ArrayList<Dog> list = new ArrayList<>();
        list.add(new Dog("小黄", 2));
        list.add(new Dog("小汪", 3));
        list.add(new Dog("小多", 4));
        System.out.println("========== 增强 for 循环遍历 ==========");
        for (Dog dog : list) {
            System.out.println(dog);
        }
        System.out.println("========== 迭代器循环遍历 ==========");
        Iterator<Dog> iterator = list.iterator();
        while (iterator.hasNext()) {
            Dog dog = iterator.next();
            System.out.println(dog);
        }
    }
}

class Dog {
    String name;
    int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 输出结果

    ========== 增强 for 循环遍历 ==========
    Dog{name='小黄', age=2}
    Dog{name='小汪', age=3}
    Dog{name='小多', age=4}
    ========== 迭代器循环遍历 ==========
    Dog{name='小黄', age=2}
    Dog{name='小汪', age=3}
    Dog{name='小多', age=4}

3、泛型概述
  1. 在类声明或实例化时制定好需要的具体类型

  2. 可以保证在编译时没有警告,运行时就不会产生 ClassCastException(类型转换异常)

  3. 通过标识表示类中某个属性的类型,某个方法的参数类型或返回值类型

4、泛型语法
  1. 自定义泛型类
java 复制代码
class 【类名】 <T> {}
  1. 自定义泛型接口
java 复制代码
class 【接口名】 <T> {}
  1. 自定泛型方法
java 复制代码
【修饰符】 <T> 【返回数据类型】 【方法名】(【参数列表】) {}
5、注意事项
  1. T 表示类型(Type 的缩写,也可以为 K、V 等),且必须是引用类型

  2. 指定泛型具体类型后,可以传入该类型或者其子类类型

  3. 泛型的简写

java 复制代码
// 完整写法
ArrayList<Dog> list = new ArrayList<Dog>();
java 复制代码
// 简写
ArrayList<Dog> list = new ArrayList<>();
  1. 这样写 ArrayList<> list = new ArrayList<>();,默认指定泛型具体类型为 Object 类型

二、自定义泛型类

1、基本介绍
java 复制代码
class 【类名】 <T> {}
2、注意事项
  1. 普通成员可以使用泛型
java 复制代码
T t;
  1. 使用泛型的数组不能初始化,因为不能确定 T 的类型,无法开辟空间,在指定泛型的具体类型后才能初始化
java 复制代码
// 错误写法
T arr[] = new T[];
  1. 静态成员不能使用泛型,因为静态成员与类相关,类加载时,静态成员无法确定泛型的具体类型,无法完成初始化

  2. 泛型的具体类型在创建对象时确定

  3. 创建对象时,没有指定泛型的具体类型,默认为 Object 类型

3、演示
  • CustomGenericsClassTest.java
java 复制代码
package com.my.test;

public class CustomGenericsClassTest {
    public static void main(String[] args) {
        Person<String> person1 = new Person<>("jack");
        Person<Integer> person2 = new Person<>(10);
        person1.show();
        person2.show();
    }
}

class Person<T> {
    T t;

    public Person(T t) {
        this.t = t;
    }

    public void show() {
        System.out.println(this.t);
    }
}
  • 输出结果

    jack
    10


三、自定义泛型接口

1、基本介绍
java 复制代码
class 【接口名】 <T> {}
2、注意事项
  1. 接口中静态成员不能使用泛型

  2. 接口的泛型的具体类型,在继承接口或实现接口时确定

  3. 没有指定泛型的具体类型,默认为 Object 类型

3、演示
  • CustomGenericInterfaceTest.java
java 复制代码
package com.my.test;

public class CustomGenericInterfaceTest {
    public static void main(String[] args) {
        new Student().show("jack");
    }
}

class Student implements Person<String> {

    @Override
    public void show(String s) {
        System.out.println(s);
    }
}

interface Person<T> {
    public void show(T t);
}
  • 输出结果

    jack


四、自定义泛型方法

1、基本介绍
java 复制代码
【修饰符】 <T> 【返回数据类型】 【方法名】(【参数列表】) {}
2、注意事项
  1. 泛型方法可以定义在普通类和泛型类中

  2. 当泛型方法被调用时,泛型的具体类型被确定

  3. public void method(T t) {} 不是泛型方法,而是使用了泛型

3、演示
  • CustomGenericMethodTest.java
java 复制代码
package com.my.test;

public class CustomGenericMethodTest {
    public static void main(String[] args) {
        Cat<String> cat = new Cat<>();
        cat.show(10, "tom");
    }
}

class Cat<K> {
    public<T> void show(T t, K k) {
        System.out.println(t.getClass());
        System.out.println(k.getClass());
    }
}
  • 输出结果

    class java.lang.Integer
    class java.lang.String


五、泛型的继承与通配符

1、基本介绍
  • 泛型不具备继承性
java 复制代码
// 错误写法
List<Object> list1 = new ArrayList<String>();
  1. <?>:支持任意类型

  2. <? extends A>:支持 A 类以及 A 类的子类,规定了泛型的上限

  3. <? super A>:支持 A 类以及 A 类的父类,规定了泛型的下限

2、演示
  • GenericExtends.java
java 复制代码
package com.my.test;

import java.util.ArrayList;
import java.util.List;

public class GenericExtends {
    public static void main(String[] args) {
        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<>();

        printList1(list1);
        printList1(list2);
        printList1(list3);
        printList1(list4);
        printList1(list5);

//        printList2(list1); // ×
//        printList2(list2); // ×
        printList2(list3);
        printList2(list4);
        printList2(list5);

        printList3(list1);
//        printList3(list2); // ×
        printList3(list3);
//        printList3(list4); // ×
//        printList3(list5); // ×
    }

    public static void printList1(List<?> list) {
        for (Object o : list) {
            System.out.println(o);
        }
    }

    public static void printList2(List<? extends AA> list) {
        for (Object o : list) {
            System.out.println(o);
        }
    }

    public static void printList3(List<? super AA> list) {
        for (Object o : list) {
            System.out.println(o);
        }
    }
}

class AA {}

class BB extends AA {}

class CC extends BB {}
相关推荐
测试开发Kevin9 分钟前
小tip:换行符CRLF 和 LF 的区别以及二者在实际项目中的影响
java·开发语言·python
笨手笨脚の11 分钟前
Redis: Thread limit exceeded replacing blocked worker
java·redis·forkjoin·thread limit
Lenyiin26 分钟前
Linux 基础IO
java·linux·服务器
松☆44 分钟前
Dart 核心语法精讲:从空安全到流程控制(3)
android·java·开发语言
编码者卢布1 小时前
【Azure Storage Account】Azure Table Storage 跨区批量迁移方案
后端·python·flask
编码者卢布1 小时前
【App Service】Java应用上传文件功能部署在App Service Windows上报错 413 Payload Too Large
java·开发语言·windows
kaikaile19951 小时前
结构风荷载理论与Matlab计算
开发语言·matlab
切糕师学AI1 小时前
ARM 汇编器中的伪指令(Assembler Directives)
开发语言·arm开发·c#
q行2 小时前
Spring概述(含单例设计模式和工厂设计模式)
java·spring
吕司2 小时前
Qt的信号与槽
开发语言·qt