前面了解到,
java基础代码
其实也就是处理一堆数据。
那其实有个bug。
就是,只能传类型确定的数据
我们在写一段代码的时候。
如果想传递和接受,不确定类型的数据,应该怎么办
于是发明了泛型这个概念。
你理解得很准!泛型(Generics)就是为了解决“代码写死类型、无法复用”这个问题而生的。
痛点演示:没有泛型时怎么办?
你想写一个打印任意类型数组的方法:
// 只能打印String数组publicstaticvoidprintString(String[]arr){...}// 只能打印Integer数组publicstaticvoidprintInteger(Integer[]arr){...}// 只能打印Double数组 → 还得再写一个... 无穷无尽有人会说:用Object不就行了?
publicstaticvoidprint(Object[]arr){for(Objecto:arr){System.out.println(o);}}但有问题:
- ❌ 取出元素必须强制转型,容易
ClassCastException - ❌ 编译期不检查类型,错误推迟到运行期
- ❌ 没有“这个数组里只能是同一种类型”的约束
泛型解决方案
把类型也当作一个“参数”传进去,用<T>表示:
// T 代表“待定的类型”publicstatic<T>voidprint(T[]arr){for(Tt:arr){System.out.println(t);}}// 使用时,自动推导类型String[]strs={"a","b"};Integer[]nums={1,2,3};print(strs);// 编译器理解为 print<String>print(nums);// 编译器理解为 print<Integer>对比效果
| 用 Object | 用泛型 | |
|---|---|---|
| 编译时类型检查 | ❌ 无 | ✅ 有 |
| 取出元素需强转 | ✅ 需要 | ❌ 不需要 |
| 代码复用度 | 一般 | 高 |
| 类型安全 | 低 | 高 |
泛型最经典的例子:集合类
// 没有泛型时(旧代码),一个篮子啥都能装Listlist=newArrayList();list.add("hello");list.add(123);// 混入不同类型Strings=(String)list.get(0);// 强转,第2个元素取出来就报错// 有泛型后,明确约束List<String>list2=newArrayList<>();list2.add("hello");// list2.add(123); // 编译错误!类型不匹配Strings2=list2.get(0);// 无需强转泛型的核心概念
1. 泛型类/接口
classBox<T>{privateTcontent;publicvoidset(Tt){content=t;}publicTget(){returncontent;}}Box<String>box=newBox<>();// 这里的String是“类型实参”2. 泛型方法(刚才展示的)
3. 类型通配符?
当你不确定具体类型,只想表达“某种类型”时:
publicvoidprintList(List<?>list){// 可以接收 List<String> / List<Integer> ...for(Objecto:list)System.out.println(o);}4. 上限extends/ 下限super
// 只允许 Number 及其子类List<?extendsNumber>nums;// 只允许 Integer 及其父类List<?superInteger>ints;一个更贴近你之前“数据处理”的例子
假设你要写一个“数据处理器”,处理不同类型的数值:
// 泛型接口interfaceProcessor<T>{Tprocess(Tinput);}// String 处理器classStringReverseimplementsProcessor<String>{publicStringprocess(Strings){returnnewStringBuilder(s).reverse().toString();}}// Integer 处理器classIntegerDoubleimplementsProcessor<Integer>{publicIntegerprocess(Integern){returnn*2;}}// 使用Processor<String>p1=newStringReverse();Stringresult=p1.process("abc");// "cba"泛型在 Java 中的“小缺点”(相对于 C++ 模板)
类型擦除:泛型只在编译期检查,运行时会擦掉类型信息。
List<String>list1=newArrayList<>();List<Integer>list2=newArrayList<>();// 下面这行会输出 true,因为运行时都是 ArrayListSystem.out.println(list1.getClass()==list2.getClass());为什么?为了兼容旧版本 Java(1.4 之前没有泛型)。
总结
| 你的问题 | 泛型的答案 |
|---|---|
| 只能传类型确定的数据 | 用<T>把类型参数化 |
| 代码写死类型 | 类型变成可传入的“参数” |
| 需要强制转型 | 编译期自动补上,类型安全 |
一句话记忆:泛型 = 类型的参数化,让你写出“处理任意类型数据的通用代码”,同时保留编译时的类型检查。