[toc]
一、为什么要有泛型
1.1泛型:标签
1.2 设计背景:
- 集合容器在设计阶段或声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在 JDK 1.5 之前 只能把元素类型设计为 Object , JDK1.5 之后使用泛型来解决。
- 泛型:把元素的类型设计成一个参数,这个类型参数叫做泛型。
- <font color=red>泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量,创建对象)确定(即传入实际的类型参数,也成为类型实参)</font>
二、在集合中使用泛型
集合接口或集合类在 jdk 5.0 时都修改为带泛型的结构
在实例化集合类时,可以指明具体的<font color=red>泛型类型</font>
指明完以后,在集合类或接口总凡是定义类或接口时,<font color=red>内部结构(比如:方法、构造器、属性)</font>使用到类的泛型的位置,都指定为实例化的泛型类型。比如 : add(E e) --> 实例化以后: add(Integer e)
注意点:泛型的类型必须是类,不能是基本数据类型,需要用到基本数据类型的位置,拿<font color=red>包装类</font>替换
如果实例化时,没有指明泛型的类型,<font color=red>默认类型为 java.lang.Object </font>类型
2.1 在集合中使用 泛型前的情况:
- 问题一:类型不安全
- 问题二:强转时,可能出现 ClassCastException
2.2 类型推断
List<Integer> list = new ArrayList<>();
三、自定义泛型结构
- 泛型类、泛型接口;泛型方法
- 要求:如果定义了类是带泛型的,建议在实例化时指明类的泛型
3.1 关于自定义泛型类、泛型接口
-
自定义泛型类
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 T getOrderT(){ return orderT; } public void setOrderT(T orderT){ this.orderT = orderT; } @Override public String toString() { return "Order{" + "orderName='" + orderName + '\'' + ", orderId=" + orderId + ", orderT=" + orderT + '}'; } } -
如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object 类型
- 经验: 泛型要用就一直用,要不用,就一直都不用
Order order = new Order(); order.setOrderT(123); order.setOrderT("ABC"); -
建议:实例化时指明类的泛型
Order<String> order1 = new Order<String>("orderAA",1001,"order:AA"); 泛型类可能有多个参数,此时应将多个参数一起放在 尖括号内。比如< E1,E2,E3>
-
泛型类的构造器如下:
public GenericClass(){}错误的是:
public GenericClass<E>(){} 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致
-
泛型不同的引用不能相互赋值
- 尽管在编译时ArrayList< String > 和 ArrayList< Integer> 是两种类型,但是,在运行时,只有一个ArrayList 被加载到 JVM 中。
ArrayList<String> list1 = null; ArrayList<Integer> list2 = null; //list1 = list2; 静态方法中不能使用类的泛型,静态方法创建早于泛型的创建,所以不能使用
-
异常类不能声明为泛型类
-
错误的代码
//public class MyException<T> extends Exception{}/*try{ }catch(T t){ }*/
-
-
不能使用new E[] .
//编译不通过 //T[] arr = new T[10]; //编译通过 T[] arr = (T[]) new Object(); -
父类有泛型,子类可以选择保留泛型也可以选指定泛型类型:
class Father<T1,T2>{ }-
子类不保留父类的泛型:按需实现
-
没有类型 擦除
class Son1 extends Father{ //等价于 class Son extends Father<Object,Object>{} } -
具体类型
class Son2 extends Father<Integer,String>{}
-
-
子类保留父类的泛型 泛型子类
-
全部保留
class Son3<T1,T2> extends Father{} -
部分保留
class Son4<T2> extends Father<Integer,T2>{}
-
-
3.2 泛型方法:
在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系
泛型方法所属的类是不是泛型类都没关系。
-
泛型方法,可以声明为静态的,原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
public <E> List<E> copyFromArrayToList(E[] arr){ ArrayList<E> list = new ArrayList<>(); for (E e : arr) { list.add(e); } return list; }
四、泛型在继承上的体现
虽然类 A 是类B 的父类,但是 G< A> 和 G< B> 二者不具备子父类关系,二者是并列关系。
-
类A 是 类B 的父类,但是 G< A> 和 G< B> 是子父类关系
AbstractList<String> list1 = null; List<String> list2 = null; ArrayList<String> list3 = null; list1 = list3; list2 = list3; List<String> list4 = new ArrayList<>();
五、通配符的使用
通配符:?
-
类A 是 类B 的父类,G< A> 和 G< B> 是没有关系的,二者共同的父类是:G<?>
@Test public void test3(){ List<Object> list1 = null; List<Integer> list2 = null; List<?> list = null; list = list1; list = list2; print(list1); print(list2); } public void print(List<?> list){ Iterator<?> iterator = list.iterator(); while(iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } } 添加:对于 List<?> 就不能向其内部添加数据。除了添加null之外
-
获取:允许读取数据,读取的数据类型为 Object
List<?> list = null; List<Integer> list1 = new ArrayList<>(); list.add(123); list = list1; Object o = list.get(0); System.out.println(o); //123
5.1 有限制条件的通配符
? extends A: G< ? extends A> 可以作为 G< A> 和 G< B> 的父类的,其中B是A的子类。
-
? super A: G< ?super A> 可以作为 G< A> 和 G< B> 的父类,其中 B 是 A 的父类。
List<? extends Person> list1 = null; List<? super Person> list2 = null; List<Student> list3 = null; List<Person> list4 = null; List<Object> list5 = null; list1 = list3; list1 = list4; //list1 = list5; //list2 = list3; list2 = list4; list2 = list5;