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

Java基础——集合进阶5

一、泛型

1.1 什么是泛型?

泛型(Generics)是 Java 5 引入的一种机制,允许在定义类、接口或方法时使用“类型参数”(Type Parameter),从而在编译期提供类型安全检查,并避免强制类型转换。

💡 简单理解:

  • 泛型 = “参数化类型”
  • 就像方法可以接收参数一样,类/方法也可以接收“类型”作为参数

 

1.2 为什么需要泛型?—— 解决三大痛点

问题没有泛型有泛型
1. 类型安全运行时才报错(如 ClassCastException编译期检查,提前发现问题
2. 强制类型转换需要手动 (String) obj 转换自动类型推断,无需强转
3. 代码复用性差为不同类型写重复代码一套代码适配多种类型

泛型 = 编译期的类型安全 + 更简洁的代码

Java中的泛型是伪泛型,集合的限定是字符串内,仅仅只是在编译的时候检查是否是字符串,当数据真正添加到集合中的时候,集合还是会把这些数据当成Object类型来处理。拿出来用的时候,底层会做一个强转成字符串类型。

Java 的泛型是“伪泛型”——只存在于编译期,运行时会被“擦除”为原始类型(Raw Type)

⚠️ 为什么设计成类型擦除?

  • 兼容 Java 5 之前的代码(向后兼容)
  • 避免 JVM 大改(不需要为每种泛型生成新类)

💡 正因如此,泛型不能用于基本类型(如 List<int> ❌),必须用包装类(List<Integer> ✅)

1.3 泛型的核心语法

泛型类

自己编写的时候,只能用<E>代表不确定是什么泛型,只用到别人调用了你写的泛型类才能确定。

package com.lkbhua.Test;import java.util.Arrays;// 带有泛型的类
public class MyArraysList<E> {Object[] obj = new Object[10];int size = 0;// E:不确定的类型,该类型在类名后面已经定义过了// e:形参的名字,变量名public boolean add  (E e){obj[size] = e;size++;return true;}public E get(int index){// 强转return (E) obj[index];}// 打印对象的时候,出现的不是地址值,而是属性值@Overridepublic String toString() {// 把数组里面所有的元素都转换成字符串拼接进行返回return Arrays.toString(obj);}
}
package com.lkbhua.Test;public class MyArraysTest1 {public static void main(String[] args) {MyArraysList<String> list = new MyArraysList<>();list.add("aaa");list.add("bbb");list.add("ccc");System.out.println(list);}
}

泛型方法

package com.lkbhua.Test;import java.util.ArrayList;public class ListUtil {private ListUtil(){}// 类中定义一个静态方法addAll,用来添加多个集合的元素// 参数一:集合// 参数二:要添加的元素//public static <E>void addAll(ArrayList<E> list ,E e1,E e2,E e3,E e4){list.add(e1);list.add(e2);list.add(e3);list.add(e4);}
}
package com.lkbhua.Test;import java.util.ArrayList;public class GenericsDemo {public static void main(String[] args) {/*泛型方法的练习:定义一个工具类:ListUtil类中定义一个静态方法addAll,用来添加多个集合的元素。*/ArrayList<String> list1 = new ArrayList<>();ListUtil.addAll(list1,"aaa","bbb","ccc","ddd");System.out.println(list1);ArrayList<Integer> list2 = new ArrayList<>();ListUtil.addAll(list2,1,2,3,4);System.out.println(list2);}
}

泛型接口

public interface Comparable<T> {int compareTo(T other);
}public class Student implements Comparable<Student> {private int id;public int compareTo(Student other) {return Integer.compare(this.id, other.id);}
}

1.4 类型参数命名规范(约定俗成)

虽然你可以用任意标识符(如 <A>),但业界通用如下:

字母含义示例
TType(类型)Box<T>
EElement(元素,常用于集合)List<E>
KKey(键)Map<K, V>
VValue(值)Map<K, V>
NNumber(数字)Calculator<N extends Number>
SUV第二、第三、第四个类型Pair<S, T>

1.5 泛型的继承

通配符 ? 用于表示“未知类型”,增强泛型的灵活性。

1️⃣ 无界通配符:<?>

public void printList(List<?> list) {for (Object obj : list) {System.out.println(obj);}
}// 可以传入任何 List
printList(new ArrayList<String>());
printList(new ArrayList<Integer>());

✅ 只能读取(当作 Object),不能添加元素(除了 null

2️⃣ 上界通配符:<? extends T> —— PECS 原则:Producer Extends

表示“T 或 T 的子类”,用于读取数据(生产者)

❌ 不能添加元素(因为不知道具体是哪个子类)
✅ 可以安全读取为 Number

3️⃣ 下界通配符:<? super T> —— PECS 原则:Consumer Super

表示“T 或 T 的父类”,用于写入数据(消费者)

package com.lkbhua.Test.泛型类test;import java.util.ArrayList;public class demo2 {public static void main(String[] args) {/* 需求:定义一个方法,形参是一个集合,但是集合中的数据类型是不确定的*/// 创建集合的对象ArrayList<Ye> list = new ArrayList<>();ArrayList<Fu> list2 = new ArrayList<>();ArrayList<Zi> list3 = new ArrayList<>();show(list);show(list2);show(list3);}// 不确定什么类型可以使用泛型// 但是有弊端:// 此时他虽然可以接受任意的数据类型,// 但是有的时候我们只希望传递部分的数据类型// 比如:Ye 、Fu 、Zi,不愿意Student或者其他类// 难道写Ye? 但是泛型不具备继承性,意味着你只能用Ye,传递Ye的集合类型才能使用该方法// 泛型的通配符:?//      ? 也表示不确定的类型//      它可以进行类型的限定//      ? extends E:表示可以传递E或者E所有的子类类型//      ? super E:  表示可以传递E或者E所有的父类类型// 表示可以传递Fu或者Fu所有的父类类型public static void show2(ArrayList<? super Fu> c) {}// 表示可以传递Ye或者Ye所有的子类类型public static void show1(ArrayList<? extends Ye> c) {}public static<E> void show(ArrayList<E> c) {}
}// 应用场景:
// 1.集合中的数据类型不确定,但是集合中的数据类型是相同的
// 2.集合中的数据类型不确定,但是集合中的数据类型是继承关系

1.6 练习

继承关系比较简单,这里只给出测试代码:

package com.lkbhua.Test.泛型类test.Text;public abstract class Animal {private String name;private int age;public Animal() {}public Animal(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;}public abstract void eat();
}
package com.lkbhua.Test.泛型类test.Text;import java.util.ArrayList;public class test {public static void main(String[] args) {/* 测试类中定义一个方法用于饲养动物:public static void keepPet(ArrayList<???> list){// 遍历集合,调用动物eat方法}需求1:该方法能养所有品种的猫,但是不能养狗需求2:该方法能养所有品种的狗,但是不能养猫需求3:该方法能养所有品种的猫和狗,但是不能传递其他的类型*/ArrayList<LiHuaCat> list1 = new ArrayList<>();ArrayList<PersianCat> list2 = new ArrayList<>();ArrayList<HuskyDog> list3 = new ArrayList<>();ArrayList<Teddy> list4 = new ArrayList<>();// 需求测试1keepPet(list1);keepPet(list2);// keepPet(list3);// keepPet(list4);// 需求测试2keepPet1(list3);keepPet1(list4);// keepPet1(list1);// keepPet1(list2);// 需求测试3keepPet2(list1);keepPet2(list2);keepPet2(list3);keepPet2(list4);}public static void keepPet(ArrayList<? extends Cat> list) {}public static void keepPet1(ArrayList<? extends Dog> list) {}public static void keepPet2(ArrayList<? extends Animal> list) {}}

声明:

分析借鉴于通义AI

以上均来源于B站@ITheima的教学内容!!!

本人跟着视频内容学习,整理知识引用

http://www.dtcms.com/a/592604.html

相关文章:

  • 没有网站怎么做cpa广告ps网站建设
  • 百度怎么注册自己的网站最有设计感的网站
  • 黑马程序员苍穹外卖(新手)Day1
  • 主从服务器的正反向声明
  • 一步一步学习使用FireMonkey动画() 用实例理解动画的运行状态
  • KUKA机械臂submit解释器将当前位置发送给C#上位机
  • 网站后台密码在哪个文件wordpress网页的源代码在哪里
  • 54_AI智能体运维部署之搭建Prometheus服务器:构建企业级监控基础设施
  • 【GitHub每日速递 】MCP 生态新工具!Registry 服务器注册服务预览版,AI 开发者部署认证全流程揭秘
  • 91、使用昇腾服务器构建FRP服务器,支持算能盒子访问
  • Vue 中实现 PDF 文件上传
  • 配置dns主从服务。要求从服务器能够定时从主服务器同步数据。
  • 中英文网站源码php网站开发8080无法访问此页面
  • 零基础如何在安服公司培训班成为网络安全工程师(黑客)
  • Oracle空间函数ST_AsText配置
  • 关系数据理论
  • 卫星姿态控制模式全解析:从基准到任务的体系化分类
  • 在百度seo快速收录要求是什么 有哪些
  • 一维前缀和与二维前缀和算法介绍及使用
  • Qwen多模态模型全解析
  • 做彩票网站要多少钱中山企业门户网站建设
  • 淘宝店铺全量商品接口实战:分类穿透采集与增量同步的技术方案
  • 【Linux】从基础到精通:内核调试与模块开发进阶之路
  • 高端品销售网站whois查询 站长工具
  • Diffusion Models与视频超分(3): 解读当前最快和最强的开源模型FlashVSR
  • 【Linux】进程间通信(二)命名管道(FIFO)实战指南:从指令操作到面向对象封装的进程间通信实现
  • 蒙古语网站建设网站制作 那种语言好
  • 阿里云效 = Jenkins + Gitlab + 免费服务器
  • Ganache-CLI以太坊私网JSON-RPC接口大全:从入门到精通
  • 免费测评RPC分布式博客平台(仅用云服务器支持高性能)