Java基础之泛型


一、为什么要有泛型(Generic)?

  泛型,JDK1.5新加入的,解决数据类型的安全性问题,其主要原理是在类声明时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化时只要指定好需要的具体的类型即可。 Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。

  1. 解决元素存储的安全性问题

  2. 解决获取数据元素时,需要类型强转的问题

public class Generic_One {

    @Test
    public void generic1(){
        //集合中没有使用泛型的情况
        List list = new ArrayList();
        list.add(100);
        list.add(91);
        list.add(81);
        //没有使用泛型,任何类型都可以加入到集合中
        list.add("Hello");

        for(int i = 0; i < list.size(); i++){
            //强转类型时候,有可能发生ClassCastException异常
            int score = (Integer)list.get(i);
        }
    }

    @Test
    public void generic2(){
        //集合中使用泛型的情况
        List list = new ArrayList();
        list.add(100);
        list.add(91);
        list.add(81);
        //没有使用泛型,任何类型都可以加入到集合中,加入泛型,加入其它类型,就直接有错误
//        list.add("Hello");

        for(int i = 0; i < list.size(); i++){
            Integer integer = list.get(i);//使用泛型,我们就不用再强转了
            System.out.println(i);
        }
    }

    @Test
    public void generic3(){
        //集合中使用泛型的情况
        Map map = new HashMap();
        map.put("1",1);
        map.put("2",2);
        map.put("3",3);
        map.put("4",4);
        //泛型是可以相互嵌套的
        Set> entries = map.entrySet();
        for (Map.Entry entry : entries) {
            System.out.println(entry.getKey() + "*****" + entry.getValue());
        }
    }
}

二、自定义泛型类

  对应泛型的方法

    方法,也可以被泛型化,不管此时定义在其中的类是不是泛型化的。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。

   泛型方法的格式: [访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常

/**
 * 泛型类
 */
public class Order {
    private String orderName;
    private int orderId;
    private T t;
    List list = new ArrayList<>();

    //声明泛型方法
    public  E getE(E e){
        return e;
    }

    //实现数组到集合的复制(并没有指定E的类型,E的类型也不一定要与T的类型一致)
    public  List copyArrayList(E[] e, List eList){
        for (E e1 : e) {
            eList.add(e1);
        }
        return eList;
    }

    public void add(){
        list.add(t);
    }

    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 getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderName='" + orderName + '\'' +
                ", orderId=" + orderId +
                ", t=" + t +
                ", list=" + list +
                '}';
    }
}

//这也是声明泛型类型的一种方法;
// (利用这个功能可以处理数据库中的表,
// 比如数据库中有很多表,每个表都是一个对象,对应表的类继承一个泛型类,用到哪个表,把对应表的类型赋给该泛型类)
class SubOreder extends Order{

}

自定义泛型类的使用

public class Generic_Two {

    //自定义泛型类的使用
    @Test
    public void generic1(){
        //当实例化泛型对象时候,指明泛型类型。指明泛型后,对应类中使用泛型的位置,都会实例化成指定的泛型类型。
        ///如果我们实例化泛型对象时候,没有指明泛型类型,实例化后,会默认泛型类型是Object。
        Order order = new Order();

        //只能放入布尔类型数据
        order.setT(true);
        Boolean t = order.getT();
        System.out.println(t);

        //调用add(),Order对象中的属性T,就添加到list中了。之后调用Order中的list,就只能添加上面定义的布尔类型了。
        order.add();
        List list = order.list;
        System.out.println(list);
    }

    @Test
    public void generic2(){

        SubOreder order = new SubOreder();
        order.add();
//已经指定了泛型类型,所以只能放Interger类型。
        List list = order.list;
        list.add(1);
        list.add(2);
        list.add(3);
    }
}

泛型方法的使用

/**
 * 泛型方法,什么时候用呢?泛型类里面,如果存在不是和泛型类一样的数据类型,我们就可以使用泛型方法。
 */
public class Generic_Three {
    @Test
    public void generic1(){
        Order order = new Order();

        //当通过泛型类对象调用泛型方法时候,我们可以给泛型方法指明数据类型。
        Integer i = order.getE(123);
        System.out.println(i);

        String strHello = order.getE("Hello");
        System.out.println(strHello);
    }

    //测试实现数组到集合的复制
    @Test
    public void generic2(){
        //虽然已经指定泛型类的数据类型,但是利用泛型方法还是可以指定要用到的数据类型
        Order order = new Order();
        Integer[] i = new Integer[]{1,2,3};
        List list = new ArrayList<>();
        List list1 = order.copyArrayList(i, list);
        System.out.println(list1);
    }
}

三、泛型和继承的关系

  如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G并不是G的子类型!

  比如:String是Object的子类,但是List并不是List的子类。

  通配符

1.使用类型通配符:? 比如:List<?> ,Map<?,?> List<?>是List、List等各种泛型List的父类。

2.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。

3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。

注意:唯一的例外是null,它是所有类型的成员。

public class Generic_Four {
    @Test
    public void generic1(){
        //可以看出,确定泛型类型的,都可以赋给没有确定泛型类型的,说明<?>是所有类型的父类
        List<?> list = new ArrayList<>();
        List obj = new ArrayList<>();
        list = obj;

        List str = new ArrayList<>();
        list = str;

        fun1(obj);//可以放入,类型相同
        fun1(str);//报错,不可以放入,类型不同

        //都是可以放入的。
        fun2(obj);
        fun2(str);

    }

    public void fun1(List list){

    }

    public void fun2(List<?> list){

    }
}


<?> 允许所有泛型的引用调用 举例:

  <? extends Number> (无穷小 , Number] 只允许泛型为Number及Number子类的引用调用

  <? super Number> [Number , 无穷大) 只允许泛型为Number及Number父类的引用调用

  <? extends Comparable> 只允许泛型为实现Comparable接口的实现类的引用调用

关于泛型:

1.对象实例化时不指定泛型,默认为:Object。

2.泛型不同的引用不能相互赋值。

3.加入集合中的对象类型必须与指定的泛型类型一致

4.静态方法中不能使用类的泛型。

5.如果泛型类是一个接口或抽象类,则不可创建泛型 类的对象。

6.不能在catch中使用泛型

7.从泛型类派生子类,泛型类型需具体化