一、泛型介绍
1.1 泛型的概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时确定(即传入实际的类型参数,也称为类型实参)。
1.2 泛型的引入背景
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection<E>,List<E>,ArrayList<E> 这个<E>就是类型参数,即泛型。
二、泛型在集合中的使用
2.1 在集合中使用泛型之前的例子
1 @Test 2 public void test1(){ 3 ArrayList list = new ArrayList(); 4 //需求:存放学生的成绩 5 list.add(78); 6 list.add(76); 7 list.add(89); 8 list.add(88); 9 //问题一:类型不安全 10 // list.add("Tom"); 11 12 for(Object score : list){ 13 //问题二:强转时,可能出现ClassCastException 14 int stuScore = (Integer) score; 15 16 System.out.println(stuScore); 17 18 } 19 20 }
图示:
2.2 在集合中使用泛型例子1
1 @Test 2 public void test2(){ 3 ArrayList<Integer> list = new ArrayList<Integer>(); 4 5 list.add(78); 6 list.add(87); 7 list.add(99); 8 list.add(65); 9 //编译时,就会进行类型检查,保证数据的安全 10 // list.add("Tom"); 11 12 //方式一: 13 // for(Integer score : list){ 14 // //避免了强转操作 15 // int stuScore = score; 16 // 17 // System.out.println(stuScore); 18 // 19 // } 20 //方式二: 21 Iterator<Integer> iterator = list.iterator(); 22 while(iterator.hasNext()){ 23 int stuScore = iterator.next(); 24 System.out.println(stuScore); 25 } 26 27 }
图示:
2.3 在集合中使用泛型例子2
1 //在集合中使用泛型的情况:以HashMap为例 2 @Test 3 public void test3(){ 4 // Map<String,Integer> map = new HashMap<String,Integer>(); 5 //jdk7新特性:类型推断 6 Map<String,Integer> map = new HashMap<>(); 7 8 map.put("Tom",87); 9 map.put("Jerry",87); 10 map.put("Jack",67); 11 12 // map.put(123,"ABC"); 13 //泛型的嵌套 14 Set<Map.Entry<String,Integer>> entry = map.entrySet(); 15 Iterator<Map.Entry<String, Integer>> iterator = entry.iterator(); 16 17 while(iterator.hasNext()){ 18 Map.Entry<String, Integer> e = iterator.next(); 19 String key = e.getKey(); 20 Integer value = e.getValue(); 21 System.out.println(key + "----" + value); 22 } 23 24 }
2.4 集合中使用泛型总结:
* ① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。
* ② 在实例化集合类时,可以指明具体的泛型类型
* ③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
* 比如:add(E e) --->实例化以后:add(Integer e)
* ④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
* ⑤ 如果实例化时,没指明泛型的类型。默认类型为java.lang.Object类型。
1 package atguigu.senior.day10.java; 2 3 import org.junit.Test; 4 5 import java.util.*; 6 7 /** 8 * 9 * 泛型的使用 10 * 1.jdk 5.0新增的特性 11 * 12 * 2.在集合中使用泛型: 13 * 总结: 14 * ① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。 15 * ② 在实例化集合类时,可以指明具体的泛型类型 16 * ③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。 17 * 比如:add(E e) --->实例化以后:add(Integer e) 18 * ④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换 19 * ⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。 20 * 21 * 3.如何自定义泛型结构:泛型类、泛型接口;泛型方法。见 GenericTest1.java 22 * 23 * @author shkstart 24 * @create 2019 上午 9:59 25 */ 26 public class GenericTest { 27 //在集合中使用泛型之前的情况: 28 @Test 29 public void test1(){ 30 ArrayList list = new ArrayList(); 31 //需求:存放学生的成绩 32 list.add(78); 33 list.add(76); 34 list.add(89); 35 list.add(88); 36 //问题一:类型不安全 37 // list.add("Tom"); 38 39 for(Object obj:list){ 40 //问题二:强转时,可能出现ClassCastException 41 int stuScore = (Integer) obj; 42 System.out.println(stuScore); 43 } 44 45 46 47 } 48 49 //在集合中使用泛型的情况:以ArrayList为例 50 @Test 51 public void test2(){ 52 ArrayList<Integer> list = new ArrayList<Integer>(); 53 54 list.add(78); 55 list.add(87); 56 list.add(99); 57 list.add(65); 58 //编译时,就会进行类型检查,保证数据的安全 59 // list.add("Tom"); 60 61 //方式一: 62 // for(Integer score : list){ 63 // //避免了强转操作 64 // int stuScore = score; 65 // 66 // System.out.println(stuScore); 67 // 68 // } 69 //方式二: 70 Iterator<Integer> iterator = list.iterator(); 71 while (iterator.hasNext()){ 72 int stuScore = iterator.next(); 73 System.out.println(stuScore); 74 } 75 76 77 } 78 79 //在集合中使用泛型的情况:以HashMap为例 80 @Test 81 public void test3(){ 82 // Map<String,Integer> map = new HashMap<String,Integer>(); 83 //jdk7新特性:类型推断 84 Map<String,Integer> map = new HashMap<>(); 85 86 map.put("Tom",87); 87 map.put("Jerry",87); 88 map.put("Jack",67); 89 90 // map.put(123,"ABC"); 91 //泛型的嵌套 92 Set<Map.Entry<String,Integer>> entry = map.entrySet(); 93 Iterator<Map.Entry<String,Integer>> iterator = entry.iterator(); 94 95 while(iterator.hasNext()){ 96 Map.Entry<String,Integer> e = iterator.next(); 97 String key = e.getKey(); 98 Integer value = e.getValue(); 99 System.out.println(key + "----" + value); 100 } 101 102 } 103 104 }
三、自定义泛型类、泛型接口、泛型方法
3.1 举例
1 【Order.java】 2 3 public class Order<T> { 4 5 String orderName; 6 int orderId; 7 8 //类的内部结构就可以使用类的泛型 9 10 T orderT; 11 12 public Order(){ 13 //编译不通过 14 // T[] arr = new T[10]; 15 //编译通过 16 T[] arr = (T[]) new Object[10]; 17 } 18 19 public Order(String orderName,int orderId,T orderT){ 20 this.orderName = orderName; 21 this.orderId = orderId; 22 this.orderT = orderT; 23 } 24 25 //如下的个方法都不是泛型方法 26 public T getOrderT(){ 27 return orderT; 28 } 29 30 public void setOrderT(T orderT){ 31 this.orderT = orderT; 32 } 33 34 @Override 35 public String toString() { 36 return "Order{" + 37 "orderName='" + orderName + '\'' + 38 ", orderId=" + orderId + 39 ", orderT=" + orderT + 40 '}'; 41 } 42 //静态方法中不能使用类的泛型。 43 // public static void show(T orderT){ 44 // System.out.println(orderT); 45 // } 46 47 public void show(){ 48 //编译不通过 49 // try{ 50 // 51 // 52 // }catch(T t){ 53 // 54 // } 55 56 } 57 58 //泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没任何关系。 59 //换句话说,泛型方法所属的类是不是泛型类都没关系。 60 //泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。 61 public static <E> List<E> copyFromArrayToList(E[] arr){ 62 63 ArrayList<E> list = new ArrayList<>(); 64 65 for(E e : arr){ 66 list.add(e); 67 } 68 return list; 69 70 } 71 } 72 73 【SubOrder.java】 74 public class SubOrder extends Order<Integer> {//SubOrder:不是泛型类 75 76 77 public static <E> List<E> copyFromArrayToList(E[] arr){ 78 79 ArrayList<E> list = new ArrayList<>(); 80 81 for(E e : arr){ 82 list.add(e); 83 } 84 return list; 85 86 } 87 88 89 } 90 91 92 //实例化时,如下的代码是错误的 93 SubOrder<Integer> o = new SubOrder<>(); 94 95 【SubOrder1.java】 96 public class SubOrder1<T> extends Order<T> {//SubOrder1<T>:仍然是泛型类 97 98 } 99 100 101 【测试】 102 @Test 103 public void test1(){ 104 //如果定义了泛型类,实例化没指明类的泛型,则认为此泛型类型为Object类型 105 //要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。 106 Order order = new Order(); 107 order.setOrderT(123); 108 order.setOrderT("ABC"); 109 110 //建议:实例化时指明类的泛型 111 Order<String> order1 = new Order<String>("orderAA",1001,"order:AA"); 112 113 order1.setOrderT("AA:hello"); 114 115 } 116 117 @Test 118 public void test2(){ 119 SubOrder sub1 = new SubOrder(); 120 //由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。 121 sub1.setOrderT(1122); 122 123 SubOrder1<String> sub2 = new SubOrder1<>(); 124 sub2.setOrderT("order2..."); 125 } 126 127 @Test 128 public void test3(){ 129 130 ArrayList<String> list1 = null; 131 ArrayList<Integer> list2 = new ArrayList<Integer>(); 132 //泛型不同的引用不能相互赋值。 133 // list1 = list2; 134 135 Person p1 = null; 136 Person p2 = null; 137 p1 = p2; 138 139 140 } 141 142 //测试泛型方法 143 @Test 144 public void test4(){ 145 Order<String> order = new Order<>(); 146 Integer[] arr = new Integer[]{1,2,3,4}; 147 //泛型方法在调用时,指明泛型参数的类型。 148 List<Integer> list = order.copyFromArrayToList(arr); 149 150 System.out.println(list); 151 }
3.2 注意点
3.3 应用场景举例
【DAO.java】:定义了操作数据库中的表的通用操作。 ORM思想(数据库中的表和Java中的类对应)
1 public class DAO<T> {//表的共性操作的DAO 2 3 //添加一条记录 4 public void add(T t){ 5 6 } 7 8 //删除一条记录 9 public boolean remove(int index){ 10 11 return false; 12 } 13 14 //修改一条记录 15 public void update(int index,T t){ 16 17 } 18 19 //查询一条记录 20 public T getIndex(int index){ 21 22 return null; 23 } 24 25 //查询多条记录 26 public List<T> getForList(int index){ 27 28 return null; 29 } 30 31 //泛型方法 32 //举例:获取表中一共有多少条记录?获取最大的员工入职时间? 33 public <E> E getValue(){ 34 35 return null; 36 } 37 38 } 39 40 【CustomerDAO.java】: 41 public class CustomerDAO extends DAO<Customer>{//只能操作某一个表的DAO 42 } 43 44 【StudentDAO.java】: 45 public class StudentDAO extends DAO<Student> {//只能操作某一个表的DAO 46 }
四、泛型在继承上的体现
1 /* 2 1. 泛型在继承方面的体现 3 4 虽然类A是类B的父类,但是G<A> 和G<B>二者不具备子父类关系,二者是并列关系。 5 6 补充:类A是类B的父类,A<G> 是 B<G> 的父类 7 8 */ 9 @Test 10 public void test1(){ 11 12 Object obj = null; 13 String str = null; 14 obj = str; 15 16 Object[] arr1 = null; 17 String[] arr2 = null; 18 arr1 = arr2; 19 //编译不通过 20 // Date date = new Date(); 21 // str = date; 22 List<Object> list1 = null; 23 List<String> list2 = new ArrayList<String>(); 24 //此时的list1和list2的类型不具子父类关系 25 //编译不通过 26 // list1 = list2; 27 /* 28 反证法: 29 假设list1 = list2; 30 list1.add(123);导致混入非String的数据。出错。 31 32 */ 33 34 show(list1); 35 show1(list2); 36 37 } 38 39 40 41 public void show1(List<String> list){ 42 43 } 44 45 public void show(List<Object> list){ 46 47 } 48 49 @Test 50 public void test2(){ 51 52 AbstractList<String> list1 = null; 53 List<String> list2 = null; 54 ArrayList<String> list3 = null; 55 56 list1 = list3; 57 list2 = list3; 58 59 List<String> list4 = new ArrayList<>(); 60 61 }
五、通配符
5.1 通配符的使用
1 /* 2 通配符的使用 3 通配符:? 4 5 类A是类B的父类,G<A>和G<B>是没关系的,二者共同的父类是:G<?> 6 7 8 */ 9 10 @Test 11 public void test3(){ 12 List<Object> list1 = null; 13 List<String> list2 = null; 14 15 List<?> list = null; 16 17 list = list1; 18 list = list2; 19 //编译通过 20 // print(list1); 21 // print(list2); 22 23 24 // 25 List<String> list3 = new ArrayList<>(); 26 list3.add("AA"); 27 list3.add("BB"); 28 list3.add("CC"); 29 list = list3; 30 //添加(写入):对于List<?>就不能向其内部添加数据。 31 //除了添加null之外。 32 // list.add("DD"); 33 // list.add('?'); 34 35 list.add(null); 36 37 //获取(读取):允许读取数据,读取的数据类型为Object。 38 Object o = list.get(0); 39 System.out.println(o); 40 41 42 } 43 44 public void print(List<?> list){ 45 Iterator<?> iterator = list.iterator(); 46 while(iterator.hasNext()){ 47 Object obj = iterator.next(); 48 System.out.println(obj); 49 } 50 }
2.涉及通配符的集合的数据的写入和读取:
见上
3.有限制条件的通配符的使用
1 /* 2 限制条件的通配符的使用。 3 ? extends A: 4 G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类 5 6 ? super A: 7 G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类 8 9 */ 10 @Test 11 public void test4(){ 12 13 List<? extends Person> list1 = null; 14 List<? super Person> list2 = null; 15 16 List<Student> list3 = new ArrayList<Student>(); 17 List<Person> list4 = new ArrayList<Person>(); 18 List<Object> list5 = new ArrayList<Object>(); 19 20 list1 = list3; 21 list1 = list4; 22 // list1 = list5; 23 24 // list2 = list3; 25 list2 = list4; 26 list2 = list5; 27 28 //读取数据: 29 list1 = list3; 30 Person p = list1.get(0); 31 //编译不通过 32 //Student s = list1.get(0); 33 34 list2 = list4; 35 Object obj = list2.get(0); 36 ////编译不通过 37 // Person obj = list2.get(0); 38 39 //写入数据: 40 //编译不通过 41 // list1.add(new Student()); 42 43 //编译通过 44 list2.add(new Person()); 45 list2.add(new Student()); 46 47 }