泛型 为什么要有泛型
泛型:标签
举例:
中药店,每个抽屉外面贴着标签
超市购物架上很多瓶子,每个瓶子装的是什么,有标签
泛型的设计背景
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList,这个就是类型参数,即泛型
泛型的概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也成为类型实参)
从JDK1.5以后,Java引入了”参数化类型(Parameterized type)“的概念,允许我们在创建集合时再指定集合元素的类型,正如List,这表明该List只能保存字符串类型的对象
JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参
为什么要有泛型呢?
解决元素存储的安全性问题,好比商品、药品标签,不会弄错
解决获取数据元素时,需要类型强制转换的问题,好比不用每回拿商品、药品都要辨别
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮
在集合中使用泛型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 package Intermediate.Generic;import org.junit.Test;import java.util.*;public class GenericTest { @Test public void Test1 () { ArrayList list = new ArrayList(); list.add(32 ); list.add(42 ); list.add(13 ); list.add(55 ); list.add("AA" ); list.add("BB" ); for (Object score : list){ int studentScore = (Integer)score; System.out.println(studentScore); } } @Test public void Test2 () { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(32 ); list.add(42 ); list.add(13 ); list.add(55 ); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { int studentScore = iterator.next(); System.out.println(studentScore); } } @Test public void Test3 () { Map<String,Integer> map = new HashMap<String,Integer>(); map.put("Tom" ,87 ); map.put("Jerry" ,87 ); map.put("Jack" ,99 ); map.put("Anny" ,78 ); Set<Map.Entry<String, Integer>> entries = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entries.iterator(); while (iterator.hasNext()){ Map.Entry<String,Integer> e = iterator.next(); String key = e.getKey(); Integer value = e.getValue(); System.out.println(key+"========>" +value); } } }
自定义泛型结构
泛型的声明 interface List 和 class GenTest 其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。 常用T表示,是Type的缩写。
泛型的实例化: 一定要在类名后面指定类型参数的值(类型)。如: List strList = new ArrayList(); Iterator iterator = customers.iterator();
T只能是类,不能用基本数据类型填充。但可以使用包装类填充
把一个集合中的内容限制为一个特定的数据类型,这就是generics背后 的核心思想
自定义泛型结构:泛型类、泛型接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 package Intermediate.Generic;public class Order <T > { String orderName; int orderId; T orderT; public Order () {} public Order (String orderName, int orderId, T orderT) { this .orderName = orderName; this .orderId = orderId; this .orderT = orderT; } public String getOrderName () { return orderName; } public void setOrderName (String orderName) { this .orderName = orderName; } public int getOrderId () { return orderId; } public void setOrderId (int orderId) { this .orderId = orderId; } public T getOrderT () { return orderT; } public void setOrderT (T orderT) { this .orderT = orderT; } } ------------------------------------------------------------------------------------------------- package Intermediate.Generic;public class SubOrder extends Order <Integer > {} ------------------------------------------------------------------------------------------------------ package Intermediate.Generic;public class SubOrader1 <T > extends Order <T > { } ------------------------------------------------------------------------------------------------------------- package Intermediate.Generic;import Intermediate.framework.Person;import org.junit.Test;import java.util.ArrayList;public class GenericTest1 { @Test public void test1 () { Order order = new Order(); order.setOrderId(123 ); order.setOrderName("As" ); Order<String> order1 = new Order<String>("orderAA" ,1001 ,"order:AA" ); order1.setOrderT("order:hello" ); } @Test public void test2 () { SubOrder sub1 = new SubOrder(); sub1.setOrderId(1122 ); SubOrader1<String> sub2 = new SubOrader1<>(); sub2.setOrderT("AAB" ); } @Test public void test3 () { ArrayList<String> list1 = null ; ArrayList<Integer> list2 = null ; list1 = list2; Person p1 = null ; Person p2 = null ; p1 = p2; } }
泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:
泛型类的构造器如下:public GenericClass(){}。 而下面是错误的:public GenericClass(){}
实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
泛型不同的引用不能相互赋值。 >尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有 一个ArrayList被加载到JVM中。
泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价 于Object。经验:泛型要使用一路都用。要不用,一路都不要用。
如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
jdk1.7,泛型的简化操作:ArrayList flist = new ArrayList<>();
泛型的指定中不能使用基本数据类型,可以使用包装类替换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class GenericTest {public static void main (String[] args) {ArrayList list = new ArrayList(); list.add("hello" ); test(list); } public static void test (ArrayList<String> list) {String str = "" ; for (String s : list) {str += s + "," ; } System.out.println("元素:" + str); } }
在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态 属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法 中不能使用类的泛型。
异常类不能是泛型的
不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity]; 参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
子类不保留父类的泛型:按需实现
没有类型 擦除
具体类型
子类保留父类的泛型:泛型子类
全部保留
部分保留
结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自 己的泛型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Father <T1 , T2 > {} class Son1 extends Father {} class Son2 extends Father <Integer , String > {} class Son3 <T1 , T2 > extends Father <T1 , T2 > {} class Son4 <T2 > extends Father <Integer , T2 > {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Father <T1 , T2 > {} class Son <A , B > extends Father {} class Son2 <A , B > extends Father <Integer , String > {} class Son3 <T1 , T2 , A , B > extends Father <T1 , T2 > {} class Son4 <T2 , A , B > extends Father <Integer , T2 > {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Person <T > {private T info;public T getInfo () {return info;} public void setInfo (T info) {this .info = info;} public Person () {} public Person (T info) {this .info = info;} }
自定义泛型结构:泛型方法
方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型 方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
泛型方法的格式: [访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
泛型方法声明泛型时也可以指定上限
不是方法里有泛型就叫泛型方法,而是在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系,换句话说,泛型方法所属的类是不是泛型类都没关系
泛型方法时可能声明为静态的,因为泛型参数是在调用方法时确定的,并非在实例化时确定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public <E>List<E> copyFromArrayToList (E[] arr) { ArrayList<E> list = new ArrayList<>(); for (E e : arr){ list.add(e); } return list; } -------------------------------------------------------------------------- @Test public void test4 () { Order<String> order = new Order<>(); Integer[] arr = new Integer[]{1 ,2 ,3 ,4 }; List<Integer> integers = order.copyFromArrayToList(arr); System.out.println(integers); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 package Intermediate.Generic.Test2;import java.util.List;public class DAO <T > { public void add (T t) { } public boolean remove (T t) { return true ; } public void update (int index, T t) { } public T getIndex (int index) { return null ; } public List<T> getForList (int index) { return null ; } public <E> E getValue () { return null ; } } ------------------------------------------------------------------------------------------------ package Intermediate.Generic.Test2;public class CustomerDAO extends DAO <Customer > {} ----------------------------------------------------------------------------------------------------- package Intermediate.Generic.Test2;public class StudentDAO extends DAO <Student > {} ------------------------------------------------------------------------------------------------------ package Intermediate.Generic.Test2;import org.junit.Test;import java.util.List;public class DAOTest { @Test public void test1 () { CustomerDAO cus = new CustomerDAO(); cus.add(new Customer()); List<Customer> forList = cus.getForList(10 ); StudentDAO stu = new StudentDAO(); stu.add(new Student()); Student index = stu.getIndex(1 ); } }
泛型在继承上的体现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package Intermediate.Generic;import org.junit.Test;import java.util.ArrayList;import java.util.Date;import java.util.List;public class GenericTest3 { @Test public void test1 () { Object obj = null ; String str = null ; obj = str; Object[] arr1 = null ; String[] arr2 = null ; arr1 = arr2; List<Object> list1 = null ; List<String> list2 = null ; } @Test public void test2 () { List<String> list1 = null ; ArrayList<String> list2 = null ; list1 = list2; } }
通配符的使用
使用类型通配符:? 比如:List ,Map List是List、List等各种泛型List的父类。
读取List的对象list中的元素时,永远是安全的,因为不管list的真实类型 是什么,它包含的都是Object。
写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中 添加对象。
将任意元素加入到其中不是类型安全的: Collection c = new ArrayList(); c.add(new Object()); // 编译时错误
因为我们不知道c的元素类型,我们不能向其中添加对象。add方法有类型参数E作为集 合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知 道那是什么类型,所以我们无法传任何东西进去。
唯一的例外的是null,它是所有类型的成员。
另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的 类型,但是我们知道,它总是一个Object。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 package Intermediate.Generic;import org.junit.Test;import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class CharacterTest { @Test public void test1 () { List<Object> list1 = null ; List<String> list2 = null ; List<?> list = null ; list = list1; list = list2; List<String> list3 = new ArrayList<>(); list3.add("AA" ); list3.add("BB" ); list3.add("CC" ); list = list3; list.add(null ); Object o = list.get(0 ); System.out.println(o); } public void print (List<?> list) { Iterator<?> iterator = list.iterator(); while (iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } } @Test public void test4 () { List<? extends Person> list1 = null ; List<? super Person> list2 = null ; List<Student> list3 = new ArrayList<>(); List<Person> list4 = new ArrayList<>(); List<Object> list5 = new ArrayList<>(); list1 =list3; list1 = list4; list2 = list5; list2 = list4; list1 = list4; Person person = list1.get(0 ); list2 = list4; Object object = list2.get(0 ); list2.add(new Person()); list2.add(new Student()); } }
1 2 3 4 5 6 7 8 9 public static <?> void test (ArrayList<?> list) {} class GenericTypeClass <?> {} ArrayList<?> list2 = new ArrayList<?>();
有限制的通配符
允许所有泛型的引用调用
通配符指定上限 上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
通配符指定下限 下限super:使用时指定的类型不能小于操作的类,即>=
举例:
(无穷小 , Number] 只允许泛型为Number及Number子类的引用调用
[Number , 无穷大) 只允许泛型为Number及Number父类的引用调用
只允许泛型为实现Comparable接口的实现类的引用调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void printCollection3 (Collection<? extends Person> coll) {Iterator<?> iterator = coll.iterator(); while (iterator.hasNext()) {System.out.println(iterator.next()); } } public static void printCollection4 (Collection<? super Person> coll) {Iterator<?> iterator = coll.iterator(); while (iterator.hasNext()) {System.out.println(iterator.next()); } }
泛型应用举例 见 Intermediate.Generic.Test3;