一、概述
- 泛型:允许在定义类、接口时通过一个标识表示类中某个属性的类型或者某个方法的返回值及参数类型。
- 这个类型参数在使用时确定;
- jdk1.5新增特性
- 泛型的类型不能是基本数据类型
二、在集合中使用泛型:
1、引入泛型:
查看一下代码
1 //在集合中使用泛型之前的情况 2 @Test 3 public void testGeneric() { 4 ArrayList list = new ArrayList(); 5 //需求:存放学生的成绩 6 list.add(34); 7 list.add(18); 8 list.add(28); 9 list.add(58); 10 list.add(38); 11 list.add(38); 12 13 //问题一、类型不安全; 14 list.add("Tom"); 15 for(Object score:list) { 16 //问题二:强转时,可能出现ClassCastException 17 // int stuScore = (Integer)score; 18 } 19 20 } 21 //在集合中使用泛型之后的情况:以Arraylist为例 22 @Test 23 public void testGeneric2() { 24 ArrayList<Integer> list = new ArrayList<Integer>(); 25 //需求:存放学生的成绩 26 list.add(34); 27 list.add(18); 28 list.add(28); 29 list.add(58); 30 list.add(38); 31 list.add(38); 32 33 //问题一、类型安全;插入String 会报错,在编译时就会进行类型检查,保证数据的安全 34 // list.add("Tom"); 35 for(Integer score:list) { 36 //问题二:避免了强转操作,不会出现ClassCastException 37 int stuScore = score; 38 System.out.println(stuScore); 39 } 40 41 Iterator<Integer> iterator = list.iterator(); 42 while(iterator.hasNext()) { 43 System.out.println(iterator.next()); 44 } 45 46 }
如以上代码:
- 没有使用泛型时,如果要在ArrayList的对象list中存放学生成绩,那么list中可以存放任意对象。如果混入String类型的对象;这会导致在取数据时,需要判断当前对象是不是Integer(如果学生成绩为Integer);否则,强转报错;
- 在使用泛型后,如果要在ArrayList的对象list中存放学生成绩,那么list只能存放Integer,不会存在混入其他类型的对象;取数据时,可以放心强转为Integer;
2、总结:
①、集合接口或者集合类在jdk5.0时都修改为带泛型的结构;
②、在实例化集合类时,可以指定泛型的类型
③、指明完以后,在集合类或接口中凡是定义类或者接口时,内部结构(方法、构造器、属性等)使用到类的泛型的位置,都指明为实例化时的泛型类型
比如add(E e),实例化后,add(Integer e);
④、注意点,泛型的类型必须是类,不能是基本数据类型(需要使用基本数据的包装类)
⑤、如果实例化时没有指明泛型,则默认为java.lang.Object类型;
三、自定义泛型结构:见Order
1、泛型类、泛型接口、泛型方法的定义
具体参加Order类(即如下代码)
1 package generic; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 /** 7 * @Description 8 * 自定义泛型结构: 9 * 泛型类、泛型接口、泛型方法; 10 * 11 * 12 * @author lixiuming 13 * @version 14 * @date 2021年4月8日下午2:26:59 15 * 16 */ 17 public class Order<T> { 18 String orderName; 19 int orderId; 20 T orderT; 21 public Order() {//构造器不需要T 22 23 } 24 public Order(String orderName,int orderId,T orderT) { 25 this.orderName = orderName; 26 this.orderId = orderId; 27 this.orderT = orderT; 28 } 29 public String getOrderName() { 30 return orderName; 31 } 32 public void setOrderName(String orderName) { 33 this.orderName = orderName; 34 } 35 public int getOrderId() { 36 return orderId; 37 } 38 public void setOrderId(int orderId) { 39 this.orderId = orderId; 40 } 41 public T getOrderT() { 42 return orderT; 43 } 44 public void setOrderT(T orderT) { 45 this.orderT = orderT; 46 } 47 @Override 48 public String toString() { 49 return "Day25Gerneric7 [orderName=" + orderName + ", orderId=" + orderId + ", orderT=" + orderT + "]"; 50 } 51 // 静态方法不能使用类的泛型 52 // public static void show(T orderT) { 53 // System.out.println(orderT); 54 // } 55 56 // public void show() { 57 // try { 58 // 59 // } catch (T e) {//编译不通过 60 // // TODO: handle exception 61 // } 62 // } 63 64 //泛型方法 65 // ①.在方法中出现了泛型的结构,泛型参数与类的方向参数没有任何关系; 66 // ②.换句话说,泛型方法所属的类是不是泛型都没有关系; 67 // ③、泛型方法可以是static的;泛型参数是在调用方法时确定的,不是在实例化时确定的 68 public <E>List<E> copyFromArrayToList(E[] arr) { 69 List<E> list = Arrays.asList(arr); 70 return list; 71 } 72 }
2、自定义泛型的使用:
1 //使用自定义泛型 2 @Test 3 public void testGeneric4() { 4 //如果定义了泛型类,实例化没有指明类的泛型,则人事此泛型类型为Object类型; 5 //要求,如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型; 6 Order<String> order1 = new Order<>("order_name",1,"order:aa"); 7 System.out.println(order1.toString()); 8 order1.setOrderT("order1:Aa"); 9 System.out.println(order1.toString()); 10 }
注意点:
①.ArrayList<Integer> list1 = new ArrayList<>();//泛型的简化操作;
②.静态方法中不能使用泛型;
③. 异常不能使用泛型
④.泛型数组声明:T[] ele = (T[])new Object[10];
⑤.子类继承父类:
①、集合接口或者集合类在jdk5.0时都修改为带泛型的结构;
②、在实例化集合类时,可以指定泛型的类型
③、指明完以后,在集合类或接口中凡是定义类或者接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型
- class Father<T1,T2>{};
- class Son<A,B> extends Father{}//等价于 class Son extends Father<Object,Object>{};
- class Son<A,B> extends Father<String,Integer>{}
- class Son<T1,T2,A,B> extends Father<T1,T2>{}//全部保留父类的泛型
- class Son<T2,A,B> extends Father<String,T2>{}//部分保留父类的泛型
关于子类继承父类的说明:
1 public class SubOrder extends Order<Integer> {//SubOrder不是泛型了 2 3 4 }
1 public class SubOrder2<T> extends Order<T> {//SubOrder任然是泛型 2 3 4 }
子类泛型对象的使用:
1 2 //使用自定义泛型 3 @Test 4 public void testGeneric5() { 5 SubOrder sub1 = new SubOrder(); 6 //由于子类在继承带泛型的父类时,指明了泛型的类型。则实例化子类对象时,不需要指明泛型类型; 7 sub1.setOrderT(111); 8 } 9 10 //使用自定义泛型 11 @Test 12 public void testGeneric6() { 13 SubOrder2<String> sub1 = new SubOrder2<>(); 14 //由于子类在继承带泛型的父类时,没有指明了泛型的类型。则实例化子类对象时,需要指明泛型类型; 15 sub1.setOrderT("123123"); 16 }
指明泛型类型后,不同的泛型对象,就是不同的对象了;
1 //使用自定义泛型 2 @Test 3 public void testGeneric7() { 4 ArrayList<Integer> list1 = new ArrayList<>(); 5 ArrayList<String> list2 = new ArrayList<>(); 6 //泛型不同的引用不能相互赋值 7 //list1 = list2; 8 9 }
四、泛型方法
1、泛型方法的定义和使用
例如:Order中的
1 //泛型方法 2 // ①.在方法中出现了泛型的结构,泛型参数与类的方向参数没有任何关系; 3 // ②.换句话说,泛型方法所属的类是不是泛型都没有关系; 4 // ③、泛型方法可以是static的;泛型参数是在调用方法时确定的,不是在实例化时确定的 5 public <E>List<E> copyFromArrayToList(E[] arr) { 6 List<E> list = Arrays.asList(arr); 7 return list; 8 }
//测试泛型方法 @Test public void testGeneric8() { Order<String> generic = new Order<>(); Integer[] arr = new Integer[] {1,2,3}; //泛型方法在调用时,指明泛型参数的类型,此泛型方法的类型和泛型方法的类型没有关系 List<Integer> list = generic.copyFromArrayToList(arr); System.out.println(list.toString()); }
2、总结:
①.在方法中出现了泛型的结构,泛型参数与类的方向参数没有任何关系;这里Order中的泛型用“T”表示,这里泛型方法中用E表示
②.换句话说,泛型方法所属的类是不是泛型都没有关系;
③、泛型方法可以是static的;泛型参数是在调用方法时确定的,不是在实例化时确定的
五、泛型在继承方面的体现
- 类A是类B的父类,G<A>和G<A>二者不具备子父类的关系,二者是完全的并列关系
- 补充:A<G>和B<G>是子父类关系
代码如下:
1 //1、泛型在继承方面的体现 2 @Test 3 public void testGeneric() { 4 Object obj = null; 5 String str = null; 6 obj = str;//多态 7 8 Object[] objArr = null; 9 String[] strArr = null; 10 objArr = strArr;//多态 11 12 List<Object> objList = null; 13 List<String> strList = null; 14 //此时的objList 和strList不具备子父类关系,所以编译不通过 15 // objList = strList; //报错 16 /* 17 * 反证法 18 * 如果两者是子父类关系 19 * 那么objList.add(123),导致混入非String的数据 20 * 21 */ 22 23 24 } 25 //A<G>和B<G>是子父类关系 26 @Test 27 public void TestGeneric1() { 28 List<String>list = null; 29 ArrayList<String> arrList = null; 30 list = arrList; 31 32 }
六、泛型中通配符的使用
1、通配符的使用
1 /* 2 *通配符的使用 3 * 通配符? 4 * ,G<A>和G<A>二者不具备子父类的关系 他们共同发父类是G<?> 5 */ 6 @Test 7 public void TestGeneric2() { 8 List<Object> list1 = null; 9 List<String> list2 = null; 10 List<?> list3 = null; 11 list3 = list1; 12 list3 = list2; 13 14 ArrayList<String> list = new ArrayList<>(); 15 list.add("aa"); 16 list.add("bb"); 17 list.add("cc"); 18 list.add("dd"); 19 list3 = list; 20 // list3.add("")//不能加元素除了加null; 21 22 Object object = list3.get(0);//获取对象为Object 23 24 25 }
此时元素的遍历:
1 public void printList(List<?> list) { 2 Iterator<?> iterator = list.iterator(); 3 while(iterator.hasNext()){ 4 Object obj = iterator.next(); 5 System.out.println(obj); 6 } 7 }
Object obj = iterator.next();用的是Object来接收;
2、总结,在List<?> list中
- 不能加元素除了加null;
- 允许读取数据,获取对象为Object
3.有限制条件的通配符
- <? extends G> 只允许泛型为G即G的子类引用调用;
- <? super G> 只允许泛型为G即G的父类引用调用;
1 public class Person { 2 3 private String name; 4 private int age; 5 public String getName() { 6 return name; 7 } 8 public void setName(String name) { 9 this.name = name; 10 } 11 public int getAge() { 12 return age; 13 } 14 public void setAge(int age) { 15 this.age = age; 16 } 17 18 }
1 package generic.test2; 2 3 public class Student extends Person{ 4 5 }
有限制条件的统配符的使用
1 /* 2 *有限制条件的通配符的使用 3 */ 4 @Test 5 public void TestGeneric3() { 6 List<? extends Person> list1 =null; 7 List<? super Person> list2 =null; 8 List<Student> list3 = null; 9 List<Person> list4 = null; 10 List<Object> list5 = null; 11 list1 = list3; 12 list1 = list4; 13 // list1 = list5;//报错 14 15 // list2 = list3; //报错 16 list2 = list4; 17 list2 = list5; 18 Person person = list1.get(0); 19 // list1.add(new Student()); 20 Object obj = list2.get(0); 21 list2.add(new Person()); 22 }
七、泛型的使用情景
比如 DAO:
基础DAO:
1 package generic.test; 2 3 import java.util.List; 4 5 public class TestDao<T>{//操作表的共性操作 6 //添加一条数据 7 public void add(T obj) { 8 9 } 10 //删除一条数据 11 public void remove(T obj) { 12 13 } 14 //修改一条数据 15 public T update(int index) { 16 return null; 17 } 18 //查询一条数据 19 public T getByIndex(int index) { 20 return null; 21 } 22 //查询多条数据 23 public List<T> getList(int index) { 24 return null; 25 } 26 27 //泛型方法 28 //举例:获取表中有多少条数据,获取最大的员工入职时间 29 public <E> E getVlaue() { 30 return null; 31 } 32 }
实体类:
Student
1 package generic.test; 2 3 public class Customer { 4 5 private String name ; 6 private int age; 7 public String getName() { 8 return name; 9 } 10 public void setName(String name) { 11 this.name = name; 12 } 13 public int getAge() { 14 return age; 15 } 16 public void setAge(int age) { 17 this.age = age; 18 } 19 public Customer(String name, int age) { 20 super(); 21 this.name = name; 22 this.age = age; 23 } 24 @Override 25 public String toString() { 26 return "Customer [name=" + name + ", age=" + age + "]"; 27 } 28 29 30 }
Customer
1 package generic.test; 2 3 public class Customer { 4 5 private String name ; 6 private int age; 7 public String getName() { 8 return name; 9 } 10 public void setName(String name) { 11 this.name = name; 12 } 13 public int getAge() { 14 return age; 15 } 16 public void setAge(int age) { 17 this.age = age; 18 } 19 public Customer(String name, int age) { 20 super(); 21 this.name = name; 22 this.age = age; 23 } 24 @Override 25 public String toString() { 26 return "Customer [name=" + name + ", age=" + age + "]"; 27 } 28 29 30 }
当Studen需要DAO时:
1 package generic.test; 2 3 import java.util.List; 4 5 public class StudentDao<T> extends TestDao<T>{ 6 7 }
当Customer需要DAO时:
1 package generic.test; 2 3 import java.util.List; 4 5 public class CustomerDao<T> extends TestDao<T>{ 6 7 }
调用:
1 @Test 2 public void test() { 3 CustomerDao<Customer> customerDao = new CustomerDao<Customer>(); 4 customerDao.add(new Customer("Joy", 1)); 5 6 StudentDao<Student> studentDao = new StudentDao<Student>(); 7 studentDao.add(new Student("Joy", 1)); 8 }
甚至:
1 @Test 2 public void test2() { 3 TestDao<Customer> customerDao = new TestDao<Customer>(); 4 TestDao<Student> studentDao = new TestDao<Student>(); 5 customerDao.add(new Customer("Joy", 1)); 6 7 studentDao.add(new Student("Joy", 1)); 8 9 10 }
使用泛型后,可以StudentDao和CustomerDao中的增删改查操作可以完全省略了;