java 基础(泛型)
什么是泛型
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在 JDK1.5 之前只能把元素类型设计为 Object,JDK1.5 之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList这个就是类型参数,即泛型。
泛型的概念
- 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。I
- 从JDK1.5以后,Java引入了“参数化类型( Parameterized type)”的概念,允许我们在创建集合时再指定集合元素的类型,正如:List,这表明该List只能保存字符串类型的对象。
- JDK1.5 改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。
泛型的使用
在集合中使用泛型总结:
- 集合接口或集合类在 jdk5.0 时都修改为带泛型的结构。
- 在实例化集合类时,可以指明具体的泛型类型
- 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器,属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
比如:add(E e) --->实例化以后:add(Integer e) - 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
- 如果实例化时,没有指明泛型的类型。默认类型为 java.lang.object类型。
自定义泛型结构:泛型类,泛型接口
- 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
- 泛型类的构造器如下: public GenericClass(){}。
而下面是错误的: public GenericClass(){} - 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
- 泛型不同的引用不能相互赋值。
尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。 - 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。经验:泛型要使用一路都用。要不用,一路都不要用。
- 如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
- jdk1.7,泛型的简化操作:ArrayList flist = new ArrayList<>();
- 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
- 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型。非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
- 异常类不能是泛型的
- 不能使用new E[]。但是可以:E[] elements =(E[])new Object[capacity];
参考: ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。 - 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
子类不保留父类的泛型:按需实现没有类型擦除
具体类型
子类保留父类的泛型:泛型子类
全部保留
部分保留
结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型
GenericTest.java
package com.klvchen.java; import org.junit.Test; import java.util.*; public class GenericTest { @Test public void test1(){ ArrayList list = new ArrayList(); //需求: 存放学生的成绩 list.add(78); list.add(76); list.add(89); list.add(88); //问题一:类型不安全 list.add("Tom"); for (Object score : list) { //问题二:强转时,可能出现CLasscastException int stuScore = (Integer) score; System.out.println(stuScore); } } // 在集合中使用泛型的情况: 以 Arraylist 为例子 @Test public void test2() { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(78); list.add(76); list.add(89); list.add(88); // 编译时,就会进行类型检查,保证数据的安全 //list.add( "Tom"); //方式一 //for (Integer score : list) { // //避免了强转操作 // int stuScore = score; // System.out.println(stuScore); //} //方式二 Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { int stuScore = iterator.next(); System.out.println(stuScore); } } // 在集合中使用泛型的情况: 以 HashMap 为例子 @Test public void test3(){ Map<String, Integer> map = new HashMap<String, Integer>(); map.put("Tom", 87); map.put("Jerry", 77); map.put("Jack", 67); //泛型的嵌套 Set<Map.Entry<String, Integer>> entry = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entry.iterator(); while (iterator.hasNext()) { Map.Entry<String, Integer> e = iterator.next(); String key = e.getKey(); Integer value = e.getValue(); System.out.println(key + " ----> " + value); } } }
GenericTest1.java
package com.klvchen.java; import org.junit.Test; import java.util.ArrayList; import java.util.List; public class GenericTest1 { @Test public void test1(){ //如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为object类型 //要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。 Order order = new Order(); order.setOrderT(123); order.setOrderT("ABC"); //建议:实例化时指明类的泛型 Order<String> order1 = new Order<>("orderAA", 1001, "Order:AA"); order1.setOrderT("AA:hello"); } @Test public void test2(){ SubOrder sub1 = new SubOrder(); //由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。 sub1.setOrderT(1122); SubOrder1<String> sub2 = new SubOrder1<>(); sub2.setOrderT("order2..."); } @Test public void test3(){ ArrayList<String> list1 = null; ArrayList<Integer> list2 = null; //泛型不同的引用不能相互赋值 //list1 = list2; Person p1 = null; Person p2 = null; p1 = p2; } @Test public void test4(){ Order<String> order = new Order<>(); Integer[] arr = new Integer[]{1, 2, 3, 4}; //泛型方法在调用时,指明泛型参数的类型。 List<Integer> list = order.copyFromArrayToList(arr); System.out.println(list); } }
Order.java
package com.klvchen.java; import java.util.ArrayList; import java.util.List; public class Order<T> { String orderName; int orderId; //类的内部结构就可以使用类的泛型 T orderT; public Order() { //编译不通过 //T[] arr = new T[10]; T[] arr = (T[]) new Order[10]; } public Order(String orderName, int orderId, T orderT) { this.orderName = orderName; this.orderId = orderId; this.orderT = orderT; } //如下的三个方法都不是泛型方法 public T getOrderT() { return orderT; } public void setOrderT(T orderT) { this.orderT = orderT; } @Override public String toString() { return "Order{" + "orderName='" + orderName + '\'' + ", orderId=" + orderId + ", orderT=" + orderT + '}'; } //静态方法中不能使用类的泛型 //public static void show(T orderT) { // System.out.println(orderT); //} // 泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。 // 换句话说,泛型方法所属的类是不是泛型类都没有关系。 // 泛型方法,可以声明为静态的。原因: 泛型参数是在调用方法时确定的。并非在实例化类时确定。 public static <E> List<E> copyFromArrayToList(E[] arr) { ArrayList<E> list = new ArrayList<>(); for (E e : arr) { list.add(e); } return list; } }
Person.java
package com.klvchen.java; public class Person { }
SubOrder.java
package com.klvchen.java; public class SubOrder extends Order<Integer> {//SubOrder:不是泛型类 }
SubOrder1.java
package com.klvchen.java; public class SubOrder1<T> extends Order<T>{ //SubOrder1<T>:仍然是泛型类 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY