java增强 -泛型

[复制链接]
发表于 2025-9-18 05:38:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
概念
界说类、接口、方法时,同时声明确一个或多个范例变量(如<E>),称为泛型类、泛型接口、泛型方法、它们统称为泛型。
语法
  1. public class ArrayList<E>{
  2.         
  3. }
复制代码
E可以吸取差别范例的数据,可以是字符串,也可以是学生类等东西。
作用:泛型提供了在编译阶段束缚所能操纵的数据范例,并自动举行查抄的本领,如允许以制止强制范例转换,及其大概出现的非常。
接下来用常见API,ArrayList聚集举例
  1. package GenericityDemo;
  2. import java.util.ArrayList;
  3. public class GenericDemo1 {
  4.     public static void main(String[] args) {
  5. //        //认识泛型
  6. //        ArrayList list = new ArrayList();           //没有使用泛型,存储任意类型数据
  7. //        list.add("hello");
  8. //        list.add(100);
  9. //        list.add(true);
  10. //        //获取数据
  11. //        for (int i = 0; i < list.size(); i++)
  12. //        {
  13. //            Object obj = list.get(i);       //必须用Object接收,因为存储的任意类型数据
  14. //            //数据转型处理
  15. //            String s = (String) list.get(i);//运行到整型时程序会崩,因此最好要统一数据类型
  16. //            System.out.println(s);
  17. //        }
  18.         ArrayList<String> list = new ArrayList<String>();       //泛型约束,只能存储String类型数据
  19.         list.add("hello");
  20.         list.add("world");
  21.         //list.add(90);     //类型不是泛型约束的类型,报错
  22.         for (int i = 0; i < list.size(); i++)
  23.         {
  24.             String s = list.get(i);     //类型确定,不用转型
  25.             System.out.println(s);      //不会出现异常
  26.         }
  27.     }
  28. }
复制代码
一、泛型类
泛型类(Generic Class) 是通过范例参数化界说的类,其焦点作用是通过范例抽象实当代码的通用性、范例安全性和机动性。
语法
  1. 修饰符 class 类名<类型变量,类型变量....>{
  2. }
复制代码
  1. public class ArrayList<E>{
  2. ......
  3. }
复制代码
示例
创建一个ArrayList聚集类(实在就是在本身的类里用之前的API,但吸取的数据范例由我们限制)
  1. package GenericityDemo;
  2. import java.util.ArrayList;
  3. public class MyArrayList <E>{
  4.     private ArrayList list = new ArrayList();
  5.     public boolean add(E e){
  6.         list.add(e);
  7.         return true;
  8.     }
  9.     public boolean remove(E e){
  10.         return  list.remove(e);
  11.     }
  12.     @Override
  13.     public String toString() {
  14.         return list.toString();
  15.     }
  16. }
复制代码
  1. package GenericityDemo;
  2. public class GenericDemo2 {
  3.     public static void main(String[] args) {
  4.         //目标:学会自定义泛型类
  5.         //需求:模拟ArrayList集合,自定义一个集合MyArrayList
  6.         MyArrayList<String> list = new MyArrayList<>();//jdk7支持后面不写泛型类型
  7.         list.add("java");
  8.         list.add("后端");
  9.         System.out.println(list);
  10.     }
  11. }
复制代码
范例变量建议利用大写的英笔墨母,常用的有:E、T、K、V等
E:常用于聚集类(如 List、Set),强调泛型参数是容器内的元素。
T:当泛型类或方法不限制具体范例时,常用 T 作为默认范例占位符。
K/V:用于键值对数据结构(如 Map)中,明确区分键和值的范例。
二、泛型接口
泛型接口(Generic Interface) 是通过范例参数化界说的接口,其焦点作用是通过范例抽象进步代码的复用性、范例安全性和机动性。
  1. 修饰符 interface 接口名<类型变量,类型变量...>{
  2. }
复制代码
  1. public interface A<E>{
  2. ...
  3. }
复制代码
示例
对学生数据/老师数据都要举行增删查改操纵。
界说一个泛型接口可以接学生或老师对象举行操纵,可以实现一个方法操纵两个角色。
  1. package GenericityDemo3;
  2. public interface Data<T> {
  3. //    void add(Student s);
  4. //    void add(Teacher t);
  5.     //类型约定死了,,只能存Student或Teacher
  6.     //利用泛型接口
  7.     void add(T t);      //此时泛型可以为Teacher也能为Student
  8.     void delete(T t);
  9.     void update(T t);
  10.     T query(int id);
  11. }
复制代码
(此示例并没有真正实现增删查改操纵,只为树模)
  1. package GenericityDemo3;
  2. public class Student {
  3. }
复制代码
  1. package GenericityDemo3;
  2. public class Teacher {
  3. }
复制代码
创造两个类用于实现Data,一个实现老师操纵,一个实现学生操纵
  1. package GenericityDemo3;
  2. public class TeacherData implements Data<Teacher>{
  3.     //操作老师数据
  4.     @Override
  5.     public void add(Teacher teacher) {
  6.     }
  7.     @Override
  8.     public void delete(Teacher teacher) {
  9.     }
  10.     @Override
  11.     public void update(Teacher teacher) {
  12.     }
  13.     @Override
  14.     public Teacher query(int id) {
  15.         return null;
  16.     }
  17. }
复制代码
  1. package GenericityDemo3;
  2. public class StudentData implements Data<Student>{
  3.     //专门操作学生对象
  4.     @Override
  5.     public void add(Student student) {
  6.     }
  7.     @Override
  8.     public void delete(Student student) {
  9.     }
  10.     @Override
  11.     public void update(Student student) {
  12.     }
  13.     @Override
  14.     public Student query(int id) {
  15.         return null;
  16.     }
  17. }
复制代码
  1. package GenericityDemo3;
  2. public class GenericDemo3 {
  3.     public static void main(String[] args) {
  4.         //目标:搞清楚泛型接口的基本应用
  5.         //需求:项目需要对学生数据/老师数据都要进行增删查改操作
  6.         StudentData studentData = new StudentData();
  7.         studentData.add(new Student());      //添加学生数据.对学生操作
  8.         Student s = studentData.query(1001);
  9.     }
  10. }
复制代码
通过泛型接口,可以为差别范例的数据界说同一的处理惩罚逻辑,同时制止强制范例转换和运行时范例错误。
三、泛型方法
泛型方法(Generic Method) 是通过在方法署名中声明范例参数来实现的方法级别泛型化。它与泛型类的主要区别在于作用范围:泛型方法的范例参数仅作用于该方法内部,而非整个类。
语法
  1. 修饰符<类型变量,类型变量,...>返回值类型 方法名(形参列表){
  2. }
复制代码
示例
如今我必要一个方法将数组的内容打印出来
  1. package GenericDemo4;
  2. public class GenericDemo4 {
  3.     //目标:理解泛型方法
  4.     public static void main(String[] args) {
  5.         //需求:打印任意数组的内容
  6.         String[] arr1 = {"hello","world","java"};
  7.         printArray(arr1);
  8.       
  9.         Student[] arr2 = new Student[3];
  10.         //printArray(arr2);//只能接收String类型数组
  11.         Student s1 = getMax(arr2);
  12.         printArray2(arr1);
  13.         getMax(arr2);       //泛型方法,可以接收任意类型的数组,避免强转
  14.     }
  15.     public static  void printArray(String[] arr)
  16.     {
  17.         for (int i = 0; i < arr.length; i++) {
  18.             System.out.println(arr[i] + " ");
  19.         }
  20.     }
  21.     public static <T> void printArray2(T[] arr)
  22.     {
  23.         for (int i = 0; i < arr.length; i++) {
  24.             System.out.println(arr[i] + " ");
  25.         }
  26.     }
  27.     public static <T> T getMax(T[] name)
  28.     {
  29.         return null;
  30.     }
  31. }
复制代码
上述代码有两个方法,一个是printArray一个是printArray2,但printArray有一个缺点就是只能打印字符串数组的内容,如果其他数组则无法吸取。因此,我们可以做一个泛型方法即printArray2,它可以吸取String数组也可以吸取Student数组,实现了一个方法办理多个题目。
四、通配符与上下限
当我们要传的参数为多个聚集,每个聚集都是继续于某个父类的子类的集适时,我们就可以通过利用通配符来吸取差别的子类聚集。(注:比方两个聚集为ArrayList<Cat>和ArrayList<Dog>,不能用ArrayList<Animal>吸取,由于他们本质上是差别的聚集,固然他们的子类继续了同一个父类)
示例
  1. package GenericDemo4;
  2. public class Car {
  3. }
复制代码
  1. package GenericDemo4;
  2. public class BYD extends Car{
  3. }
复制代码
  1. package GenericDemo4;
  2. public class BMW extends Car{
  3. }
复制代码
  1. package GenericDemo4;
  2. import java.util.ArrayList;
  3. public class GenericDemo5 {
  4.     public static void main(String[] args) {
  5.         //目标:理解通配符和上下限
  6.         ArrayList<BYD> byds = new ArrayList<>();
  7.         byds.add(new BYD());
  8.         byds.add(new BYD());
  9.         byds.add(new BYD());
  10.         //go(byds);           //报错不具备通用性
  11.         ArrayList<BMW> bmws = new ArrayList<>();
  12.         bmws.add(new BMW());
  13.         bmws.add(new BMW());
  14.         bmws.add(new BMW());
  15.         go(bmws);
  16.     }
  17.     //模拟极品飞车游戏
  18.     //虽然比亚迪和宝马是Car的子类,但ArrayList<BYD>和ArrayList<BMW>和ArrayList<Car>是不同的,因此不能用多态
  19. //    public static void go(ArrayList<BMW> cars)
  20. //    {
  21. //
  22. //    }
  23.     //通配符,在使用泛型时代表一切类型
  24.    
  25.     public static void go(ArrayList<?> cars)
  26.     {
  27.     }
  28. }
复制代码
然而,如果这么写这个方法能吸取的聚集范围又太大了,连猫和狗都可以进这个开汽车的方法,因此必要利用上下限来加以限制。
? extends Car ?能吸取Car大概Car的子类(泛型上限)
? super Car ?能吸取Car大概Car的父类(泛型下限)
  1. package GenericDemo4;
  2. import java.util.ArrayList;
  3. public class GenericDemo5 {
  4.     public static void main(String[] args) {
  5.         //目标:理解通配符和上下限
  6.         ArrayList<BYD> byds = new ArrayList<>();
  7.         byds.add(new BYD());
  8.         byds.add(new BYD());
  9.         byds.add(new BYD());
  10.         //go(byds);           //报错不具备通用性
  11.         ArrayList<BMW> bmws = new ArrayList<>();
  12.         bmws.add(new BMW());
  13.         bmws.add(new BMW());
  14.         bmws.add(new BMW());
  15.         go(bmws);
  16.     }
  17.     //模拟极品飞车游戏
  18.     //虽然比亚迪和宝马是Car的子类,但ArrayList<BYD>和ArrayList<BMW>和ArrayList<Car>是不同的,因此不能用多态
  19. //    public static void go(ArrayList<BMW> cars)
  20. //    {
  21. //
  22. //    }
  23.     //通配符,在使用泛型时代表一切类型
  24.     //为了防止将狗,猫等不属于车的类型传入方法,使用泛型的上下限限制
  25.     //? extends Car ?能接收Car或者Car的子类(泛型上限)
  26.     //? super Car ?能接收Car或者Car的父类(泛型下限)
  27.     public static void go(ArrayList<? extends Car> cars)
  28.     {
  29.     }
  30. }
复制代码
如许,就能将这个方法限制在吸取汽车类的子类了。
五、泛型支持的数据范例
泛型不支持根本的数据范例,即int,double等数据范例,缘故原由是泛型工作在编译阶段,编译竣过后体系会举行泛型擦除,全部范例都会厘革为Object范例。而Object是对象范例,吸取的是对象,数字等范例不是对象,因此不吸取根本的数据范例。为了可以或许兼容根本的数据范例,java的库里添加了包装类,用于将根本数据范例变为对象。
包装类有8种。
int -> Integer char -> Character ,别的的都是首字母巨细写。
  1. package GenericDemo5;
  2. import java.util.ArrayList;
  3. import java.util.Objects;
  4. public class GenericDemo5 {
  5.     public static void main(String[] args) {
  6.         //目标:搞清楚泛型和集合不支持基本数据类型,只能支持对象类型(引用类型)
  7.         //ArrayList<double> list = new ArrayList<>();   //报错,不支持
  8.         //泛型擦除:泛型工作在编译阶段,等编译后泛型就没用了,所以泛型在编译后都会被擦除。所有类型会恢复为Object类型。
  9.         //Object是对象类型,不能直接指向某个数,只能指向某个对象。
  10.         //因此要使用包装类,比如Integer、Double、Character、Boolean等。
  11.         //包装类的作用:把基本数据类型包装成对象类型。
  12.         //ArrayList<int> list = new ArrayList<>();
  13.         //list.add(12);     //12不是对象,报错
  14.         ArrayList<Integer> list = new ArrayList<>();
  15.         list.add(12);           //自动装箱,把基本数据类型12包装成Integer对象。
  16.         int rs= list.get(0);        //自动拆箱,把Integer对象12拆箱成基本数据类型。
  17.         //把基本数据类型变成包装类对象
  18.         //手工包装
  19.         //Integer i = new Integer(12);//从jdk9开始,这个方法被淘汰了(过时)
  20.         //推荐用valueOf的原因:valueOf方法中缓存了-128~127的数值,重复创建这个范围内的数字时不会创建新对象,而是直接返回缓存中的对象。
  21.         //缓存对象范围是-128~127,如果超过这个范围,就会创建新的对象。
  22.         //更优雅,节约内存。
  23.         Integer i = Integer.valueOf(12);    //此时i为对象,里面存储的是12
  24.         Integer i2 = Integer.valueOf(12);
  25.         System.out.println(i == i2);        //ture, 因为i和i2是同一个对象
  26.         //自动装箱
  27.         Integer i3 = 12;        //与Integer i = Integer.valueOf(12)一个意思
  28.         Integer i4 = 12;
  29.         System.out.println(i3 == i4);
  30.         //自动拆箱:把包装类型的对象直接给基本类型的数据
  31.         int i5 = i3;
  32.         System.out.println(i5);
  33.         System.out.println("========================================================");
  34.         //包装类新增的功能
  35.         //1、把基本类型的数据变成字符串
  36.         int j=23;
  37.         String rs1 = Integer.toString(j);
  38.         System.out.println(rs1+1);      //不是24,说明已经是字符串
  39.         Integer i6=j;
  40.         String rs2 = i6.toString();
  41.         System.out.println(rs2+1);
  42.         String  rs3 = i6+"";        //直接加""转换为字符串
  43.         System.out.println(rs3+1);
  44.         System.out.println("========================================================");
  45.         //把字符串数值转换成对应的基本数据类型
  46.         String str = "123";
  47.         int i1 = Integer.parseInt(str);
  48.         int i9 = Integer.valueOf(str);
  49.         System.out.println(i1+1);
  50.         System.out.println(i9+1);
  51.     }
  52. }
复制代码

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表