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

【从0开始学习Java | 第15篇】泛型

在这里插入图片描述

文章目录

        • 引言:你是否也遇到过这样的麻烦?
        • 一、泛型是什么?一句话说清核心
        • 二、为什么需要泛型?3个核心优势
        • 三、泛型怎么用?3个核心场景
      • 通配符的基本形式
      • 通配符的使用场景
      • 通配符与泛型方法的区别
      • 总结
        • 四、泛型的细节和注意事项
        • 结语:泛型的本质是“抽象”

引言:你是否也遇到过这样的麻烦?

假设你想写一个“求两个数之和”的函数,一开始可能这么写:

// 整数相加
public int addInt(int a, int b) {return a + b;
}// 浮点数相加
public double addDouble(double a, double b) {return a + b;
}

如果还要支持字符串拼接、自定义对象合并呢?难道要为每种类型都写一个几乎一样的函数?

这就是泛型要解决的核心问题:让代码摆脱“类型绑定”,实现“一次定义,多种类型通用”。今天我们就来聊聊泛型——这个让代码更简洁、更安全的“利器”。

一、泛型是什么?一句话说清核心

泛型(Generics)是编程语言中一种“参数化类型”的机制,简单说就是:在定义类、接口或方法时,不指定具体类型,而是用一个“占位符”代替,等到使用时再传入实际类型

比如上面的“相加”需求,用泛型可以写成:

// 泛型方法:T是类型占位符,代表“任意类型”
public <T> T add(T a, T b) {// 实际实现需根据类型处理,这里仅示意return ...;
}

调用时传入整数、字符串等类型,编译器会自动“替换”占位符,保证类型匹配。

二、为什么需要泛型?3个核心优势
  1. 消除重复代码,提高复用性
    不用为每种类型写重复逻辑,一套代码适配多种场景。比如Java集合框架(ListMap)就是泛型的典型应用:List<String> 存字符串,List<Integer> 存整数,底层逻辑却完全复用。

  2. 编译时类型检查,减少运行时错误
    没有泛型时,集合中可以放任意类型的对象,取出来时需要强制类型转换,容易出现 ClassCastException有了泛型后,编译器会在编译阶段检查类型是否匹配,提前规避错误

    List<String> list = new ArrayList<>();
    list.add(123); // 编译报错:不能添加整数到字符串列表
    
  3. 代码更清晰,可读性更高
    看到 List<User> 就知道这个列表存的是用户对象,不用猜类型,也不用看注释。

三、泛型怎么用?3个核心场景
  1. 泛型方法:在方法上定义类型参数,适用于“单个方法需要适配多种类型”的场景(即方法中的形参类型不确定时)。

方案1: 在方法申明上定义自己的泛型【只能在本方法中使用,其他方法不能使用】

public class MyArrayList {Object[] obj = new Object[10];int size;public <E> boolean add(E e){obj [size]=e;size++;return true;}
}

方案2:在类名后面定义泛型【公共泛型,所有方法都可以用】

public class MyArrayList<E> {Object[] obj = new Object[10];int size;public boolean add(E e){obj [size]=e;size++;return true;}public static <E> void addAll(ArrayList<E>list, E e1,E e2) {list.add(e1);list.add(e2);}
}
  1. 泛型类/接口:在类或接口定义时声明类型参数,适用于“整个类的逻辑与类型相关”的场景(如集合、容器)。

在这里插入图片描述

例 - 泛型类:自定义一个泛型栈:

public class GenericStack<T> {private List<T> elements = new ArrayList<>();public void push(T element) {elements.add(element);}public T pop() {if (elements.isEmpty()) return null;return elements.remove(elements.size() - 1);}
}
// 使用时指定类型,无需强转
GenericStack<String> stringStack = new GenericStack<>();
stringStack.push("hello");
String str = stringStack.pop(); // 直接得到String类型

例子2 - 泛型类:
在这里插入图片描述

在这里插入图片描述
两种实现方式:

  • 实现类给出具体的类型
public class MyArrayList3 implements List<String> {.....
}public class Generics {public static void main(String args[]){MyArrayList3 list3 = new MyArrayList2();}
}
  • 实现类延续泛型,创建实现类对象时再确定泛型
public class MyArrayList2<E> implements List<E>{.....
} public class Generics {public static void main(String args[]){MyArrayList2<String> list2 = new MyArrayList2<>();}
}
  1. 泛型通配符:在Java泛型中,通配符(Wildcard)是一种特殊的语法,用于灵活地表示“未知类型”,解决泛型类型参数的灵活性问题。它的核心作用是在不明确指定具体类型的情况下,限制泛型的范围,让代码更通用。

通配符的基本形式

通配符用 ? 表示,常见的使用场景有三种:

  1. 无界通配符(Unbounded Wildcard)?
    表示“任意类型”,但无法获取泛型的具体类型信息(编译时会擦除)。

    示例:

    // 可以接收任何泛型类型的List
    public void printList(List<?> list) {for (Object obj : list) {System.out.println(obj);}
    }
    

    注意:使用无界通配符的集合不能添加元素(除了null),因为编译器不知道具体类型,无法保证类型安全。

  2. 上界通配符(Upper Bounded Wildcard)? extends T
    表示“T类型或T的子类”,限制了泛型的上限。

    示例:

    // 接收Number及其子类(如Integer、Double)的List
    public double sum(List<? extends Number> list) {double total = 0;for (Number num : list) {total += num.doubleValue(); // 可调用Number的方法}return total;
    }
    

    特点:

    • 可以读取元素(类型为T或其子类,可安全向上转型为T)。
    • 不能添加元素(除了null),因为无法确定具体是哪个子类(例如List<? extends Number>可能是List<Integer>List<Double>,添加DoubleList<Integer>会出错)。
  3. 下界通配符(Lower Bounded Wildcard)? super T
    表示“T类型或T的父类”,限制了泛型的下限。

    示例:

    // 接收Integer及其父类(如Number、Object)的List
    public void addIntegers(List<? super Integer> list) {list.add(10); // 可以添加Integer(或其子类,如Integer本身)list.add(20);
    }
    

    特点:

    • 可以添加元素(类型为T或其子类,因为父类集合可以接收子类对象)。
    • 读取元素时,只能确定是Object类型(因为父类可能是任意上层类型,无法安全转型为具体类型)。

通配符的使用场景

  • 无界通配符:当代码不依赖泛型的具体类型,仅需要“任意类型的泛型”时(如打印集合)。
  • 上界通配符:当需要读取泛型对象,并使用其上层父类的方法时(如计算数值总和)。
  • 下界通配符:当需要写入泛型对象,且对象类型是某个类的子类时(如向集合添加特定类型元素)。

通配符与泛型方法的区别

通配符和泛型方法都能实现泛型的灵活性,但适用场景不同:

  • 通配符更适合限制泛型范围(如? extends Number),简化参数声明。
  • 泛型方法(如<T> void method(List<T> list))更适合需要在方法中复用具体类型的场景(如返回与参数同类型的对象)。

总结

通配符通过?? extends T? super T三种形式,解决了泛型类型的灵活性问题,核心是在“类型安全”和“通用性”之间找到平衡:

  • 上界通配符:只能读,不能写(除null)。
  • 下界通配符:只能写(特定类型),读时只能作为Object
  • 无界通配符:只读(Object),基本不能写

四、泛型的细节和注意事项
  1. 类型擦除:泛型信息仅在编译时存在,运行时会被“擦除”为原始类型(如 List<String> 运行时会变成 List)。因此不能用 new T()T.class 这样的写法,也不能判断 obj instanceof List<String>所以,实际上来说,Java中的泛型其实是伪泛型,只是在编译时检查,运行时仍然换回Object类型处理,处理完毕后再次强转回对应的类型输出而已

  2. 泛型数组限制:不能直接创建泛型数组(如 new T[10] 不允许),但可以声明泛型数组引用(T[] array

  3. 静态方法不能使用类的泛型参数:类的泛型参数属于实例,静态方法需自己声明泛型参数

  4. 泛型中不能写基本数据类型(在泛型擦除时,基本数据类型无法转换成Object类型)

  5. 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类型

  6. 如果不写泛型,类型默认是Object

  7. 泛型不具备继承性,但是数据具备继承性
    在这里插入图片描述

结语:泛型的本质是“抽象”

泛型的核心思想是“对类型进行抽象”——就像函数用参数抽象值,泛型用类型参数抽象具体类型。它让代码从“为具体类型服务”升级为“为一类逻辑服务”,这也是面向对象“抽象”思想的延伸。

在这里插入图片描述
在这里插入图片描述


如果我的内容对你有帮助,请 点赞 评论 收藏 。创作不易,大家的支持就是我坚持下去的动力!
在这里插入图片描述


文章转载自:

http://BSYG636Q.hnrqn.cn
http://trhRTINk.hnrqn.cn
http://XBDKHhXN.hnrqn.cn
http://J54wLtT3.hnrqn.cn
http://pbYN6mAM.hnrqn.cn
http://tByvm7Bo.hnrqn.cn
http://cwlaEoUh.hnrqn.cn
http://FOljKxws.hnrqn.cn
http://sLzKUSIE.hnrqn.cn
http://O7P2cDXo.hnrqn.cn
http://nMIxKvxw.hnrqn.cn
http://FxiibMxp.hnrqn.cn
http://PlRRC1gc.hnrqn.cn
http://ouOV9cxa.hnrqn.cn
http://kuoVk6MO.hnrqn.cn
http://eCxikjIx.hnrqn.cn
http://8eVZzVis.hnrqn.cn
http://7pehh6rB.hnrqn.cn
http://NiB4gFeJ.hnrqn.cn
http://zWlNCURB.hnrqn.cn
http://oVqgxG91.hnrqn.cn
http://Hh4qBIr2.hnrqn.cn
http://YF6JhMNt.hnrqn.cn
http://xZ1Q493g.hnrqn.cn
http://8YE0KXz5.hnrqn.cn
http://3aa3oGuY.hnrqn.cn
http://LWs1tPfZ.hnrqn.cn
http://Lx4iWpB2.hnrqn.cn
http://atFsRurn.hnrqn.cn
http://91hJeLNg.hnrqn.cn
http://www.dtcms.com/a/377160.html

相关文章:

  • JavaSE丨深入剖析:从JVM类加载到反射编程的核心机制
  • eclipse中web项目编译后的lib里面jar为空问题处理
  • 非阻塞式等待和进程程序替换
  • 科技信息差(9.10)
  • Laya使用VideoNode动态加载视频,可以自定义播放视频此处以及位置
  • AWStats 网站日志分析工具简单介绍和常见问题
  • 在Windows 11上配置Cursor IDE进行Java开发
  • iOS App 混淆与反编译防护 iOS代码保护、ipa文件安全加固与应用逆向分析对抗全流程指南
  • 【Android】设置让输入框只能输入数字
  • 大数据探索性分析——抽样技术应用
  • 2010-2022 同等学力申硕国考:软件工程简答题真题汇总
  • 在FreeSSL上申请免费证书,将http改成https
  • micropython的属性式GPIO控制
  • 华为FreeBuds 7i没有弹窗如何解决?
  • 循环+函数
  • leetcode-hot100 11.盛水最多容器
  • pyspark 从postgresql读取数据
  • Spring Cloud Alibaba快速入门03-OpenFeign
  • Chrome 插件开发入门技术文章大纲
  • 小说写作中的时间轴管理:基于 Vue 3 的事序图技术实现
  • 计算机视觉与深度学习 | 计算机视觉中线特征提取与匹配算法综述
  • DAPP智能合约系统:技术解析与实现指南
  • AutoTrack-IR-DR200仿真导航实验详解:为高校打造的机器人学习实践平台
  • [模块教学]VK16K33_8×16LED矩阵屏的驱动以及技术文档,矩阵屏, 详细配置说明
  • BMT-370:开启智能楼宇通信新时代
  • stm32中 中断和事件的区别
  • Android开发入门系列教程
  • CSS 权重(优先级规则)
  • 快速搭建open-webui
  • Qt 信号-槽函数(signal - slot)