所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类 型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如, 继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实 际的类型参数,也称为类型实参)。
从JDK1.5以后,Java引入了“参数化类型(Parameterized type)”的概念, 允许我们在创建集合时再指定集合元素的类型,正如:List,这表明 该List只能保存字符串类型的对象。
JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持, 从而可以在声明集合变量、创建集合对象时传入类型实参。
解决元素存储的安全性问题
解决获取数据元素时,需要类型强制转换的问题
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException异常。同时,代码更加简洁、健壮。
- ArrayList list = new ArrayList();
- //问题1:集合中什么类型的数据都可以添加
- list.add(113);
- list.add("嘻戏i");
- list.add(true);
-
- for (Object o: list) {
- //问题:2:强转时,可能出现ClassCastException
- Integer num = (Integer) list;
- System.out.println(num);
- }
单列集合
- ArrayList
list1 = new ArrayList<>(); - list1.add(123);
- list1.add(12);
- list1.add(121);
- //编译时,就会进行类型检查,保证数据的安全
- // list1.add("嘻戏i");
- Iterator
iterator = list1.iterator(); - while (iterator.hasNext()){
- //避免了强转操作
- Integer num = iterator.next();
- System.out.println(num);
- }
双列集合
- Map
map = new HashMap<>();//jdk7新特性:类型推断(后边的泛型不用再写一次) - map.put("张三",23);
- map.put("李四",24);
- map.put("王五",25);
-
- //泛型的嵌套
- Set
> entrySet = map.entrySet(); - Iterator
> iterator1 = entrySet.iterator(); - while (iterator1.hasNext()){
- Map.Entry
entry = iterator1.next(); - String key = entry.getKey();
- Integer value = entry.getValue();
- System.out.println(key + "--->" + value);
- }
① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。
② 在实例化集合类时,可以指明具体的泛型类型
③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。比如:add(E e) --->实例化以后:add(Integer e)
④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。
自定义泛型类
- public class OrderT
{ -
- private String orderName;
- private int orderAge;
- //类的内部结构可以使用类的泛型做数据类型
- private T orderT;
-
- public OrderT(){
- //构造器里生成一个固定长度的泛型类型数组写法
- //编译不通过
- // T[] arr = new T[10];
- //编译通过
- T[] arr = (T[]) new Object[10];
- }
-
- public OrderT(String orderName,int orderAge,T orderT){
- this.orderName = orderName;
- this.orderAge = orderAge;
- this.orderT = orderT;
- }
-
- //该方法都不是泛型方法
- public T getOrderT(){
- return orderT;
- }
- //该方法都不是泛型方法
- public void setOrderT(T orderT){
- this.orderT = orderT;
- }
-
- //该方法都不是泛型方法
- @Override
- public String toString() {
- return "OrderT{" +
- "orderName='" + orderName + '\'' +
- ", orderAge=" + orderAge +
- ", orderT=" + orderT +
- '}';
- }
-
- //静态方法中不能使用类的泛型(因为静态方法比类加载的早,因此不能确定泛型的类型)
- // public static void show(T orderT){
- // System.out.println(orderT);
- // }
-
- //异常类不能是泛型的
- public void show(){
- //编译不通过
- // try{
- // }catch(T t){
- // }
-
- }
-
- }
泛型的实例化
- //如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型
- //要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。
- OrderT orderT = new OrderT();
- //可以设置任意类型的自定义类orderT属性参数
- orderT.setOrderT("嘻戏i");
- orderT.setOrderT(123);
-
- //建议在实例化的时候指明类的泛型
- OrderT
orderT1 = new OrderT<>(); - //编译不通过
- // orderT1.setOrderT("嘻戏i");
- orderT1.setOrderT(123);
自定义泛型接口
- public interface InterfaceOrderT
{ -
- //抽象方法
- void show(T t);
- }
-
- //实现接口的demo1类——规定数据类型为String
- class Demo1 implements InterfaceOrderT
{ -
- @Override
- public void show(String s) {
- System.out.println(s);
- }
- }
-
- //实现接口的demo2类——规定数据类型为Object
- class Demo2
implements InterfaceOrderT{ -
- @Override
- public void show(T t) {
- System.out.println(t);
- }
- }
泛型的实例化
- Demo1 demo1 = new Demo1();
- demo1.show("嘻戏i");
-
- Demo2 demo2 = new Demo2();
- demo2.show(111);
- demo2.show("嘻戏i");
1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:
2. 泛型类的构造器如下:public GenericClass(){}。
错误写法:public GenericClass
3. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
4. 泛型不同的引用不能相互赋值。
尽管在编译时ArrayList
5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。经验:泛型要使用一路都用。要不用,一路都不要用。
6. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
7. jdk1.7,泛型的简化操作:ArrayList
8. 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
9. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
10. 异常类不能是泛型的
11. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
子类不保留父类的泛型:按需实现
>没有类型 擦除
>具体类型
子类保留父类的泛型:泛型子类
>全部保留
>部分保留
结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型
- class Father
{ - }
- // 子类不保留父类的泛型
- // 1)没有类型 擦除
- }
- // 2)具体类型
- }
- // 子类保留父类的泛型
- // 1)全部保留
- class Son3
extends Father { - }
- // 2)部分保留
- class Son4
extends Father { - }
自定义泛型方法
方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。 在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
泛型方法的格式:[访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
- public static
List copyFromArrayToList(E[] arr){ - List
list = new ArrayList<>(); - for(E e : arr){
- System.out.println(e);
- }
- return list;
- }
泛型方法调用
- Integer[] arr = new Integer[]{1,2,3,4,5};
- //泛型方法在调用时,指明泛型参数的类型。
- List
integers = OrderT.copyFromArrayToList(arr); - System.out.println(integers);
- List
- List
list2 = new ArrayList(); - //此时的list1和list2的类型不具有子父类关系
- //编译不通过
- // list1 = list2;
-
- /*
- 反证法:
- 假设list1 = list2;
- list1.add(123);导致混入非String的数据。出错。
- */
- AbstractList
list1 = null; - List
list2 = null; - ArrayList
list3 = null; -
- list1 = list3;
- list2 = list3;
类A是类B的父类,G和G是没有关系的,二者共同的父类是:G>
- List
- List
list2 = null; -
- List> list = null;
- list = list1;
- list = list2;
- List> list = null;
-
- List
list3 = new ArrayList<>(); - list3.add("AA");
- list3.add("BB");
- list3.add("CC");
- list = list3;
- //编译不通过
- // list.add("DD");
- // list.add('?');
-
- list.add(null);
- List> list = null;
- ArrayList
list3 = new ArrayList<>(); - list3.add("嘻戏i");
- list3.add("嘻戏ii");
- list3.add("嘻戏iii");
-
- list = list3;
- Object o = list.get(0);
- System.out.println(o);
- public void print(List> list){
- Iterator> iterator = list.iterator();
- while (iterator.hasNext()){
- Object o = iterator.next();
- System.out.println(o);
- }
注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
- public static > void test(ArrayList> list){
- }
注意点2:编译错误:不能用在泛型类的声明上
- class GenericTypeClass>{
- }
注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
ArrayList> list2 = new ArrayList>();
? extends A:
G extends A> 可以作为G和G的父类,其中B是A的子类
- List extends Person> list1 = null;
-
- List
- List
list3 = new ArrayList<>(); - //Student是Person的子类
- List
list4 = new ArrayList<>(); -
- // extends Person>泛型类型范围:(无穷小 , Person]
- // list1 = list2;
- list1 = list3;
- list1 = list4;
-
- //读取数据:类型必须为Person
- Person person = list1.get(0);
- // Student student = list1.get(0);
-
- //写入数据:不允许写入类型 因为它的最小范围为无限小
- // list1.add(new Person());
使用时指定的类型必须是继承某个类,或者实现某个接口
? super A:
G super A> 可以作为G和G的父类,其中B是A的父类
- List super Person> list1 = null;
-
- List
- List
list3 = new ArrayList<>(); - //Student是Person的子类
- List
list4 = new ArrayList<>(); -
- // extends Person>泛型类型范围:[Person , 无穷大)
- list1 = list2;
- list1 = list3;
- // list1 = list4;
-
- //读取数据:类型必须为Object
- Object object = list1.get(0);
- // Person person = list1.get(0);
-
- //写入数据:允许添加Person类及其相关的子类
- // list1.add(new Object());
- list1.add(new Person());
- list1.add(new Student());