Java学习之路(十二):泛型

一、为什么要有泛型

  • 泛型:标签(JDK1.5新增的特性)
  • 泛型的设计背景:集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能吧元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的。例如关于这个元素如何保存,如何管理是确定的,因此此时把元素的类型设计成一个参数,这个类型参数就叫做泛型。Collection,List,ArrayList,这个就是类型参数,即泛型
  • 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(如继承、实现接口或用这个类型声明变量、创建对象)确定(即传入实际的类型参数,也称为类型实参)

二、在集合中使用泛型

  1. 集合接口或集合类在JDK5.0时都修改为带泛型的结构、
  2. 在实例化集合类时,可以指明具体的泛型类型
  3. 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(属性、方法、构造器)使用到类的泛型的位置,都指定为实例化的泛型类型
  4. 注意点:泛型的类型必须是类,不能是基本数据类型,如果需要用到基本数据类型,则使用包装类替换
  5. 如果实例化,没有指明泛型的类型,则默认使用java.lang.Object类型。
package com.zc.test.fanxing;

import org.junit.Test;

import java.util.*;

/**
 * @author ZC
 * @Description  测试方向的使用
 * @date 2020-07-06 12:03
 */
public class FanXingTest {

    @Test
    public void test(){
        //以ArrayList举的例子
        ArrayList<Integer> list = new ArrayList<>();
        list.add(123);
        list.add(12);
        list.add(124);
        list.add(122);
        list.add(112);
        /*
        //编译时就会进行类型检查,保证数据的安全
        list.add("Tom")
         */
        System.out.println(list);
        for (Integer score:list
             ) {
            System.out.println(score);
        }
        //Iterator也可以使用泛型
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

    @Test
    public void test2(){
        //以HashMap举的例子
        Map<String,Integer> map = new HashMap<>();
        map.put("Tom",123);
        map.put("Jerry",423);
        map.put("Jack",153);

        //泛型的嵌套
        Set<Map.Entry<String,Integer>> entry = map.entrySet();
    }
}

三、自定义泛型结构:泛型类、泛型接口、泛型方法

1、泛型类、泛型接口的注意点

  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内,比如<E1,E2,E3>
  2. 泛型类的构造器如下:public GenericClass(){}
    错误的写法:public GenericClass<E>(){}
  3. 实例化后,操作原来泛型的位置必须与指定的泛型类型一致
  4. 泛型不同的引用不能相互复制
    尽管在编译时,ArrayList<String>和ArrayList<Integer>是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中
  5. 泛型如果不指定,将被擦除,泛型对应的类型均应按照Object处理,但不等价于Object。经验:泛型要使用就一路都用。要不用,一路都不用。
  6. 如果泛型结构是一个接口或抽象类,则不可以创建泛型类的对象
  7. JDK1.7,泛型的简化操作:ArrayList<Integer> = new ArrayList<>();
  8. 泛型的指定中不能使用基本数据类型,可以使用包装类
  9. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型,非静态方法的参数类型、返回类型。但在静态方法中不能使用类的泛型
  10. 异常类不能是泛型
  11. 不能使用 new E[]。但是可以:E[] elements = (E[])new Object[capacity]
    参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组
  12. 父类有泛型,子类可以选择保留泛型也可以选择指定泛型
    1. 子类不保留父类的泛型:按需实现
      1. 没有类型 擦除
      2. 具体类型
    2. 子类保留父类的泛型:泛型子类
      1. 全部保留
      2. 部分保留

在这里插入图片描述
在这里插入图片描述

  • 结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型

2、泛型类、泛型接口

package com.zc.test.fanxing;

/**
 * @author ZC
 * @Description 自定义泛型类
 * @date 2020-07-06 14:11
 */
public class Order<T> {
    
    String name;
    Integer id;
    T orderT;

    public Order() {
    	//错误写法:T[] arr = new T[10]
    	T[] arr = (T[]) new Object[10];
    }

    public Order(String name, Integer id, T orderT) {
        this.name = name;
        this.id = id;
        this.orderT = orderT;
    }

//如下两个方法都不是泛型方法
    public T getOrderT() {
        return orderT;
    }
    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }
}

package com.zc.test.fanxing;

/**
 * @author ZC
 * @Description  由于继承的时候指明了泛型的具体类型,所以子类SubOrder1在实例化时不需要指明泛型的具体类型
 * @date 2020-07-06 14:15
 */
public class SubOrder1 extends Order<Integer>{
}

package com.zc.test.fanxing;

/**
 * @author ZC
 * @Description 继承的时候保留了泛型,则SubOrder2<T>仍然是泛型类
 * @date 2020-07-06 14:15
 */
public class SubOrder2<T> extends Order<T>{
}


3、泛型方法

  • 泛型方法:在方法中出现了泛型的结构,但是方法中的泛型参数与类的泛型参数没有任何关系
  • 换句话说:泛型方法所属的类是不是泛型类都没有关系
  • 泛型方法在调用时,指明泛型参数的类型,所以泛型方法也可以是静态方法
package com.zc.test.fanxing;

import java.util.ArrayList;
import java.util.List;

/**
 * @author ZC
 * @Description 自定义泛型类
 * @date 2020-07-06 14:11
 */
public class Order<T> {

    String name;
    Integer id;
    T orderT;

    public Order() {
    }

    //如下两个方法都不是泛型方法
    public T getOrderT() {
        return orderT;
    }
    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }

    //泛型方法:在方法中出现了泛型的结构,但是方法中的泛型参数与类的泛型参数没有任何关系
    //换句话说:泛型方法所属的类是不是泛型类都没有关系
    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> 二者不具备子父类关系,二者是并列关系,二者共同的父类是 G\<?>
  • 补充:类A是类B的父类,A<G> 是 B<G> 的父类

五、通配符的使用

1. 普通通配符

  • 通配符:?(英文格式下的问号)
  • 添加(写入):对于List<?>就不能向其内部添加数据,除了添加null之外
  • 获取(读取):允许读取List<?>内部的数据,读取的数据类型是Object
package com.zc.test.fanxing;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author ZC
 * @Description  '?'通配符测试
 * @date 2020-07-06 15:34
 */
public class GenericTest {
    @Test
    public void test(){
        List<Object> list = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();

        //编译出错,详细请见泛型继承性的体现
        //list = list2;

        //通配符的使用,G<A>和G<B>是没有关系的,二者共同的父类是G<?>
        List<?> list3 = null;
        list3 = list;
        list3 = list2;

        print(list);
        print(list2);
    }

    public void print(List<?> list){
        Iterator<?> iterator = list.iterator();
        while (iterator.hasNext()) {
            Object o = iterator.next();
            System.out.println(o);
        }
    }
}

2. 有限制条件的通配符

  • ? extends A:G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类
  • ? super A:G<? super A>可以作为G<A>和G<B>的父类,其中B是A的父类
  • 添加(写入):G<? extends A>不能写入,G<? super A>可以吸入A类的对象或A的子类的对象
  • 获取(读取):G<? extends A>返回的数据类型是A,G<? super A>返回的数据类型是Object
  • 总结:子类可以赋值给父类
package com.zc.test.fanxing;

import org.junit.Test;

import java.util.List;

/**
 * @author ZC
 * @Description
 * @date 2020-07-06 15:48
 */
class Person{
}

class Student extends Person{
}

public class GenericTest2 {
    /**
     * ? extends A   G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类
     * ? super A     G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类
     */

    @Test
    public void test() {
        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;
    }

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值