当前位置: 首页 > news >正文

Java学习第八部分——泛型

目录

一、概述

(一)定义

(二)作用

(三)引入原因

二、使用

(一)类

(二)接口

(三)方法

三、类型参数

(一)命名

(二)限制

四、类型擦除

(一)概念

(二)影响

五、通配符

(一)`?`(无界通配符)

(二)`? extends T`(上界通配符)

(三)`? super T`(下界通配符)

六、高级用法

(一)泛型数组

(二)泛型与反射

(三)泛型与匿名内部类

七、idea项目实战

(一)打开idea新建Java项目

(二)编写GenericCollectionUtil类

(三)创建测试类

(四)编译运行代码,注意切换到测试类文件

(五)运行结果如下

(六)代码解释如下

1. GenericCollectionUtil 类

2. GenericCollectionUtilTest`类


一、概述

(一)定义


        Java泛型(Generics)是指在定义类、接口或方法时,可以指定一个或多个类型参数(Type Parameters),这些类型参数在使用时会被具体的类型替换。它允许程序员在编写代码时延迟确定数据类型,从而提高代码的复用性和安全性。

(二)作用

       Java泛型是一种强大的特性,它可以让代码更加通用、安全和复用。通过泛型类、泛型接口和泛型方法,可以编写出能够处理多种类型数据的代码。同时,泛型的类型参数、类型擦除和通配符等概念也需要深入理解,以便正确使用泛型。

(三)引入原因


1. 类型安全
   - 在没有泛型的情况下,集合类(如ArrayList)只能存储Object类型的对象。当从集合中取出元素时,需要进行强制类型转换。例如:

     List list = new ArrayList();
     list.add("Hello");
     String str = (String) list.get(0);

     这种方式存在类型安全问题,因为list可以存储任何类型的对象,如果添加了其他类型的对象,如`list.add(123);`,在取出时进行强制类型转换就会抛出`ClassCastException`异常。
   - 使用泛型后,可以在编译阶段就检查类型是否正确。例如:

     List<String> list = new ArrayList<String>();
     list.add("Hello");
     String str = list.get(0); // 不需要强制类型转换

     如果尝试添加非String类型的对象,如`list.add(123);`,编译器会报错。
2. 代码复用
   - 泛型可以让我们编写更加通用的代码。例如,一个泛型类可以处理多种类型的数据,而不需要为每种数据类型都编写一个类。

二、使用

(一)类


1. 定义泛型类
   - 泛型类的定义格式为:

     class 类名<类型参数> {
         // 类的成员可以使用类型参数
     }

   - 例如,定义一个简单的泛型类Box:

     public class Box<T> {
         private T t;

         public void set(T t) {
             this.t = t;
         }

         public T get() {
             return t;
         }
     }

2. 使用泛型类
   - 使用时需要指定具体的类型参数。例如:

     Box<String> stringBox = new Box<String>();
     stringBox.set("Hello");
     String str = stringBox.get();

     Box<Integer> integerBox = new Box<Integer>();
     integerBox.set(123);
     Integer num = integerBox.get();
 

(二)接口


1. 定义泛型接口
   - 泛型接口的定义格式与泛型类类似:
        interface 接口名<类型参数> {
         // 接口的成员可以使用类型参数
     }

   - 例如,定义一个泛型接口Comparable:
 
     public interface Comparable<T> {
         int compareTo(T o);
     }

2. 实现泛型接口
   - 实现泛型接口时,可以指定具体的类型参数,也可以不指定。例如:
  
     public class Person implements Comparable<Person> {
         private String name;

         public Person(String name) {
             this.name = name;
         }

         @Override
         public int compareTo(Person o) {
             return this.name.compareTo(o.name);
         }
     }
 

(三)方法


1. 定义泛型方法
   - 泛型方法的定义格式为:
 
     <类型参数> 返回值类型 方法名(参数列表) {
         // 方法体
     }

   - 例如,定义一个泛型方法swap:

     public <T> void swap(T[] array, int i, int j) {
         T temp = array[i];
         array[i] = array[j];
         array[j] = temp;
     }

2. 使用泛型方法
   - 调用泛型方法时,可以省略类型参数,编译器会自动推断。例如:

     String[] strArray = {"a", "b"};
     swap(strArray, 0, 1);
 

三、类型参数

(一)命名


- 通常使用单个大写字母作为类型参数的名称,常见的有:
  - `T`:Type
  - `E`:Element
  - `K`:Key
  - `V`:Value
  - `N`:Number

(二)限制


1. 有界类型参数
   - 可以通过`extends`关键字对类型参数进行限制,使其只能是某个类或接口的子类型。例如:

     public <T extends Number> void print(T t) {
         System.out.println(t);
     }

     这样,`T`只能是`Number`或其子类的实例。
2. 无界类型参数
   - 如果没有对类型参数进行限制,那么它就是一个无界类型参数,可以是任何类型。例如:

     public <T> void print(T t) {
         System.out.println(t);
     }
 

四、类型擦除

(一)概念


- 在Java中,泛型是通过类型擦除实现的。也就是说,在运行时,泛型类型会被擦除,所有的类型参数都会被替换为它们的边界(如果有边界的话),如果没有边界,则替换为`Object`。
- 例如,`List<String>`和`List<Integer>`在运行时都会被擦除为`List`。

(二)影响


1.无法获取泛型类型参数
   - 由于类型擦除,无法在运行时获取泛型类型参数的具体类型。例如:

     List<String> list = new ArrayList<String>();
     System.out.println(list.getClass().getTypeParameters()); // 输出的是一个空的TypeVariable数组

2.泛型方法的重载
   - 由于类型擦除,泛型方法的签名在运行时是相同的,因此不能仅根据类型参数的不同来重载泛型方法。例如:
  
     public <T> void print(T t) {
         System.out.println(t);
     }

     public <E> void print(E e) {
         System.out.println(e);
     }

     这是不允许的,因为编译器无法区分这两个方法。

五、通配符

(一)`?`(无界通配符)


- 表示任意类型。例如:

  public void printList(List<?> list) {
      for (Object obj : list) {
          System.out.println(obj);
      }
  }

  这个方法可以接受任何类型的`List`作为参数。

(二)`? extends T`(上界通配符)


- 表示类型参数是`T`或`T`的子类型。例如:

  public void printNumberList(List<? extends Number> list) {
      for (Number num : list) {
          System.out.println(num);
      }
  }

  这个方法可以接受`List<Number>`、`List<Integer>`、`List<Double>`等作为参数。

(三)`? super T`(下界通配符)


- 表示类型参数是`T`或`T`的父类型。例如:

  public void addNumberToList(List<? super Integer> list) {
      list.add(123);
  }

  这个方法可以接受`List<Integer>`、`List<Number>`、`List<Object>`等作为参数。

六、高级用法

(一)泛型数组


- 不能创建泛型数组,例如`new T[10]`是不允许的。但是可以通过其他方式来实现类似的功能。例如:

  public <T> T[] createArray(Class<T> clazz, int size) {
      return (T[]) Array.newInstance(clazz, size);
  }
 

(二)泛型与反射


- 可以通过反射来获取泛型类型参数。例如:
 
  Type type = new TypeReference<List<String>>() {}.getType();
  if (type instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
      System.out.println(actualTypeArguments[0]); // 输出:class java.lang.String
  }
 

(三)泛型与匿名内部类


- 可以在匿名内部类中使用泛型。例如:

  public static void main(String[] args) {
      List<String> list = new ArrayList<String>() {
          {
              add("Hello");
          }
      };
      System.out.println(list.get(0));
  }
 

七、idea项目实战

(一)打开idea新建Java项目

ps:如果需要,可以在pom.xml文件中添加其他依赖。对于这个简单的项目不需要额外的依赖。

(二)编写GenericCollectionUtil

package com.example.util;import java.util.ArrayList;
import java.util.List;public class GenericCollectionUtil<T> {private List<T> list = new ArrayList<>();/*** 添加元素到集合** @param element 要添加的元素*/public void add(T element) {list.add(element);}/*** 获取集合中的元素** @param index 元素的索引* @return 索引对应的元素*/public T get(int index) {return list.get(index);}/*** 清空集合*/public void clear() {list.clear();}/*** 获取集合大小** @return 集合的大小*/public int size() {return list.size();}@Overridepublic String toString() {return list.toString();}
}

(三)创建测试类

package com.example.util;public class GenericCollectionUtilTest {public static void main(String[] args) {// 测试字符串集合GenericCollectionUtil<String> stringUtil = new GenericCollectionUtil<>();stringUtil.add("Hello");stringUtil.add("World");System.out.println("String Collection: " + stringUtil);System.out.println("Element at index 0: " + stringUtil.get(0));stringUtil.clear();System.out.println("After clear: " + stringUtil);// 测试整数集合GenericCollectionUtil<Integer> intUtil = new GenericCollectionUtil<>();intUtil.add(1);intUtil.add(2);intUtil.add(3);System.out.println("Integer Collection: " + intUtil);System.out.println("Element at index 1: " + intUtil.get(1));intUtil.clear();System.out.println("After clear: " + intUtil);}
}

(四)编译运行代码,注意切换到测试类文件

ps:红色箭头所示两种点击都可以实现

(五)运行结果如下

(六)代码解释如下

1. GenericCollectionUtil 类

       这个类是一个泛型工具类,用于处理不同类型的数据集合操作。它使用了 Java 的泛型特性,使得同一个类可以处理多种类型的数据。

成员变量
- `private List<T> list = new ArrayList<>();`
  - 这是一个泛型列表,用于存储集合中的元素。`T` 是一个类型参数,表示集合中元素的类型。

方法
- `public void add(T element)`
  - 这是一个泛型方法,用于向集合中添加元素。`element` 是要添加的元素,其类型由 `T` 决定。
  
- `public T get(int index)`
  - 这是一个泛型方法,用于从集合中获取指定索引位置的元素。返回值的类型也是 `T`。
  
- `public void clear()`
  - 这是一个非泛型方法,用于清空集合中的所有元素。
  
- `public int size()`
  - 这是一个非泛型方法,用于获取集合中元素的数量。
  
- `@Override public String toString()`
  - 这是一个重写的方法,用于返回集合的字符串表示形式。它直接返回 `list.toString()`,即集合中所有元素的字符串表示。

2. GenericCollectionUtilTest`类

这个类是一个测试类,用于验证 `GenericCollectionUtil` 类的功能。

主要逻辑
- 创建 `GenericCollectionUtil` 类的实例,分别用于字符串和整数类型的集合。
- 向集合中添加元素,并打印集合的字符串表示形式。
- 获取并打印指定索引位置的元素。
- 清空集合,并打印清空后的集合表示形式。

输出示例
- 打印字符串集合和整数集合的初始状态。
- 打印指定索引位置的元素。
- 打印清空集合后的状态。

优点
- **泛型使用**:通过使用泛型,代码更加通用和灵活,可以处理多种类型的数据集合。
- **简洁性**:代码简洁明了,易于理解和维护。
- **功能明确**:每个方法的功能都很明确,易于使用。

缺点
- **功能有限**:目前只实现了添加元素、获取元素、清空集合和获取集合大小的功能,还可以扩展更多功能,如删除元素、查找元素等。
- **异常处理**:在获取元素时,如果索引超出范围,会抛出 `IndexOutOfBoundsException` 异常。可以考虑添加异常处理逻辑,提高代码的健壮性。

扩展功能建议
- **删除元素**:添加一个方法,用于从集合中删除指定索引位置的元素。
- **查找元素**:添加一个方法,用于查找集合中是否存在指定的元素。
- **排序**:添加一个方法,用于对集合中的元素进行排序。
- **过滤**:添加一个方法,用于对集合中的元素进行过滤,返回满足条件的元素集合。

http://www.lryc.cn/news/579410.html

相关文章:

  • git 中删除提交历史
  • 代码随想录算法训练营第四十五天|动态规划part12
  • Fiddler中文版抓包工具在后端API调试与Mock中的巧用
  • 应用在核电行业的虚拟现实解决方案
  • Laravel8中调取腾讯云文字识别OCR
  • 【前端开发】Uniapp分页器:新增输入框跳转功能
  • SpringCloud系列(49)--SpringCloud Stream消息驱动之实现生产者
  • Rubber Band Algorithm 应力及反作用力测试
  • 运维打铁: 企业运维开发痛点之解决方案
  • ModuleNotFoundError: No module named ‘onnxruntime‘
  • 【免费.NET方案】CSV到PDF与DataTable的快速转换
  • 图论基础算法入门笔记
  • MySQL 8.0 OCP 1Z0-908 题目解析(18)
  • 深度学习2(逻辑回归+损失函数+梯度下降)
  • 在 VSCode 中高效配置自定义注释模板 (无需插件)
  • Python 中如何使用 Conda 管理版本和创建 Django 项目
  • Flowable多引擎架构搭建方案
  • 车载以太网-IP 掩码 vlan 端口
  • 前端的一些报错
  • Odoo 中国特色高级工作流审批模块研发
  • 编程基础:成员函数
  • 【LUT技术专题】3DLUT压缩-CLUT
  • 朝鲜APT组织使用Nim语言恶意软件对macOS发起隐秘Web3与加密货币攻击
  • .net wpf混淆
  • uniapp 使用ffmpeg播放rtsp
  • QT常用类和模块
  • Qt宝藏库:20+实用开源项目合集
  • Java——初始guava(1)
  • 【python】OOP:Object-Oriented Programming
  • Linux基本命令篇 —— tar命令