泛型
导读
- 泛型在各集合接口中的应用
- 自定义类和自定义泛型方法需注意的点,静态泛型方法内部不可以使用类的泛型(生命周期不同),非静态泛型方法的内部才可以使用类的泛型
- 继承关系下,子类对于父类泛型的几种继承情况
- 通配符是重点,特别是两种有线通配符**<? extends A> / < ? super B>**
引用
-
标签
-
设计背景
-
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在jdk5之前只能把元素类型设计为Object;
-
jdk5之后使用泛型来解决:因为这个时候除了元素的类型不确定,其他部分是确定的,例如关于这个元素如何保存,如何管理等,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。
-
Collection< E> ,List< E>,ArrayList 这个< E>就是类型参数,就是泛型
-
一、集合中的泛型
-
在集合中使用泛型
① 集合接口或集合类在jdk5.0时都改为带泛型的结构
② 在实例化集合类时,可以指明具体的泛型类型
③指明完后,在集合类或接口中凡是定义类或接口时,内部结构使用到类的泛型位置,都被指定为实例化的泛型类型。
④ 泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替代
⑤ 如果实例化时,没有指明泛型的类型,默认类型为 java.lang.Object -
代码示例
@Test // 泛型在List接口中的使用 public void test1() { // 实例化对象时指定具体的泛型类型为Integer ArrayList<Integer> list = new ArrayList<>(); list.add(123); list.add(346); list.add(3456); // 内部结构使用到类的泛型的位置,都与在实例化时指定的泛型类型一致 // Iterator本身就是一个带泛型的接口,所以被对象调用时也被指定 Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } @Test // 泛型在Map接口中的使用 public void test2() { Map<String, Integer> map = new HashMap<>(5); map.put("Tom",59); map.put("Susan",19); map.put("Jock",29); // 泛型的嵌套 Set<Map.Entry<String,Integer>> set = map.entrySet(); for (Map.Entry<String,Integer> entry : set) { System.out.println(entry); } // entrySet()方法的源码 transient Set<Map.Entry<K,V>> entrySet; public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new HashMap.EntrySet()) : es; } }
二、自定义泛型类/接口、自定义泛型方法
-
自定义泛型类
① 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。例如: <E1,E2,E3>
② 泛型类的构造器不需要加泛型
public GenericsClass() {}
③ 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致
④ 泛型不同的引用不能互相赋值
⑤ 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
经验:泛型要使用一路都用。要不用,一路都不用
⑥ 如果泛型类是一个接口或抽象类,则不可创建泛型类的对象
-⑦ jdk7之后,泛型操作简单化:
ArrayList<Fruit> flist = new ArrayList<>();
⑧ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但:非泛型类的静态方法中不能使用泛型,因为泛型在实例化时才会被指定,静态是随着类的加载而加载的,生命周期不同。
⑨ 异常类不能是泛型
⑩ 泛型的数组的声明方式
// 实际上就是造了一个Object类的数组,然后强制转换成T[]类型 T[] arr = (T[]) new Object[capacity];
⑾ 自定义泛型类代码示例
public class GenericsClassTest<T> { private String name; private int age; // 1.带泛型的属性 private T orderT; public GenericsClassTest() {} // 2.带泛型参数的构造器 public GenericsClassTest(String name, int age, T orderT) { this.name = name; this.age = age; this.orderT = orderT; } // 3.带泛型的非静态方法 public T getOrderT() { return orderT; } public void setOrderT(T orderT) { this.orderT = orderT; } }
-
自定义泛型类的子类
① 示例泛型父类如下:
class Father<T,E> { //fields;constructor;getter/setter;methods··· }
② 子类继承了父类的泛型
class Sub<T,E> extends Father<T,E> {}
③ 子类部分保留了父类的泛型
class Sub<String,E> extends Father<T,E> {}
④ 子类自定义了泛型,擦除了父类的泛型
class Sub<A,B> extends Father {}
⑤ 父类的泛型擦除了
class Sub extends Father {}
⑥ 父类的泛型指定了具体类型
class Sub extends Father<String,Integer> {}
-
自定义泛型方法
① 泛型方法中的泛型结构、泛型参数可以与类的泛型参数没有任何关系
② 方法可不可以是泛型结构,与所在的类是不是泛型没有关系
③ 泛型方法可以是静态的,因为方法的泛型在被调用时才会确定类型,与static不冲突,**但是其本身及内部结构不允许使用所在类的泛型,**因为类的泛型需要实例化的时候才能被确定。
如果静态泛型方法使用了类的泛型,那么类本身在调用静态方法时,该静态方法的泛型应该是什么类型呢?所以冲突了;如果静态泛型方法没有使用类的泛型,那么它在被类调用时,就可以马上指定泛型的具体类型
④ 在泛型方法的头部的返回值类型/void前面应该加上 ,表明 E 代表一个泛型类型,而不是某个类名为E的类
-
代码示例
public class GenericsTest<T> { // 泛型方法测试 public static void main(String[] args) { GenericsTest<String> gen = new GenericsTest<>(); Integer[] arr = {1, 2, 3, 4, 5}; List<Integer> list = gen.fromArrayToList(arr); System.out.println(list); } // 泛型方法的创建 public static <E> List<E> fromArrayToList(E[] arr1) { ArrayList<E> list1 = new ArrayList<>(); for (E e : arr1){ list1.add(e); } return list1; } }
-
-
自定义泛型类和泛型方法的应用 - - 对于数据库的操作
① 表的共性操作DAO
class DAO<T> { public void add(T t) { } // 未确定T的类型,先返回null public List<T> getForList(int index) { return null; } // 泛型方法 public <E> E getValue() { return null; } }
② 数据库中的一张表对应一个类 - - 例如:Customer类
class Customer { }
③ 针对某张表的DAO
class CustomerDAO<T> extends DAO<T> { }
④ 操作示例
public class ExtendsTest { public static void main(String[] args) { // 确定了CustomerCao泛型的类型为:Customer CustomerCao<Customer> cao1 = new CustomerCao<>(); Customer c1 = new Customer(); // 通过该对象调的父类中的泛型结构都必须时Customer类型的 cao1.add(c1); // 调用泛型方法 Long num = cao1.<Long>getValue(); String str = cao1.<String>getValue(); } }
三、泛型在继承方面的体现 及 通配符 < ?>
-
类A是类B是父类,但是G不是G 的父类,是并列关系,没有继承关系
类A是类B是父类,A
仍然是B 的父类 ① 代码示例
@Test public void test1() { // 字符串是不可改变的,当有新值赋给obj1时,会指向其它内存空间 Object obj1 = null; String str1 = "aa"; obj1 = str1; System.out.println(obj1); // 数组也具有不可变性 Object[] obj2 = null; String[] str2 = {"AA","BB"}; obj2 = str2; for (Object o : obj2) { System.out.println(o); } // List集合指定泛型类型后,也和数组类似, List<Object> obj = null; List<String> str = null; // 下式不成立,详见内存解析图 // obj = str; }
② 内存图解释
-
通配符
类A是类B的父类,G 和 G 是并列关系,G<?> 是它们共同的父类
@Test public void test2() { List<Object> obj = null; List<String> str = null; List<?> list = null; list = obj; list = str; }
-
通配符的使用
用于读取数据,不允许添加数据,因为作为父类,没有指定具体类型,会造成指定集合内数据类型冲突
@Test public void test2() { List<?> list = null; // 读取数据 ArrayList<String> list3 = new ArrayList<>(); list3.add("AA"); list3.add("BB"); list3.add("CC"); list = list3; for (Object o : list) { System.out.println(o); } }
-
有限通配符
① < ? extends A >
泛型类型是A本身或A的子类,只能读取,不能写入
② < ? super A >
泛型类型是A本身或A的父类,可以写入A本身或A的子类,多态的体现
@Test public void test4() { List<Object> obj = null; List<DAO> dao = null; List<CustomerDao> cus = null; // ? extends 相当于 (-∞ ,DAO) List<? extends DAO> list1 = null; list1 = cus; list1 = dao; // ? super 相当于 (DAO ,+∞) List<? super DAO> list2 = null; list2 = dao; list2 = obj; } } class DAO {} class CustomerDao extends DAO {}