Fork me on GitHub

第17章 泛型

泛型

1.为什么需要使用泛型

1.存储任意类型的数据在集合中,但是取出来都是Object类型,此时就得强转
2.约束存储到集合中的元素必须是相同的数据类型(相同的数据类型才能做比较,如TreeSet类)
3.设计一个点(point)类,来封装坐标位置,要求坐标位置支持String类型,Integer类型,Double类型。普通做法是定义三个不同类型的类,这样做太不优雅了

2.定义和使用通用泛型类

1.定义
泛型是从java5开始支持的新语法。
什么是泛型呢?
1.广泛通用的类型
2.代码模板中类型不确定,谁调用该段代码谁就指明类型是什么

泛型类:直接在类/接口上定义的泛型
使用泛型:
1.保证前后类型相同
List<String> list = new ArrayList<String>();//该List集合中只能存储String类型的元素

2.因为前后类型相同,所有java7开始,退出泛型的菱形语法<>,即后一个可以不写
List<String> list = new ArrayList<>();

3.泛型不存在继承的关系(错误如下)
List<Object> list = new ArrayList<String>();

4.从此以后,使用集合都得使用泛型来约束该集合中元素的类型
5.通过反编译发现:泛型其实也是语法糖,底层依旧没有泛型,而且依然使用强转

示例:

/**
 * Created by cenyu on 16-11-21.
 * 点坐标对象
 * 在本类中,T表示一种类型,该类具体类型是什么,由调用者来决定
 * 通用几种泛型代指.T:type   E:element    K:key    V:value
 * 需要定义的类型用尖括号<>在定义的时候括起来,下面就可以将T当做一个类型来用
 */
public class Ponit<T> {
    private T x;
    private T y;

    public T getX() {
        return x;
    }

    public T getY() {
        return y;
    }

    public void setX(T x) {
        this.x = x;
    }

    public void setY(T y) {
        this.y = y;
    }
}


public class PointDemo {
    public static void main(String[] args) {
        //使用String类型,Point类中T全部用String替换
        //调用的时候需要在等号做右边同时定义类型
        Ponit<String> p = new Ponit<String>();
        p.setX("3");
        String x1 = p.getX();
        System.out.println(x1);
        //使用Integer类型
        //同上
        //使用Double类型
        //同上
    }
}

3.定义和使用泛型方法

泛型方法:在方法上声明泛型
以下情况使用:
1.泛型类中的泛型只能使用于非静态方法,如果需要给静态方法设置泛型,此时使用泛型方法
2.泛型类中的泛型适用于整个类中多个方法,有时候只对某一个方法设置泛型即可

一般的,把自定义的泛型作为该方法的返回类型才有意义。而且此时的泛型必须是有参数设置进来的。如果没有参数来设置泛型的具体类型,此时的方法一般返回设计为Object即可。

/**
 * 测试泛型方法
 * Created by cenyu on 16-11-22.
 */
public class GebericAdd {
    //定义一个泛型方法,规定返回值为double
    public <T extends Number> double add(T t1, T t2){
        double sum = 0.0;
        sum = t1.doubleValue()+t2.doubleValue();
        return sum;
    }

    public static void test(){
        GebericAdd addTest = new GebericAdd();

        int num1=3;
        int num2=4;
        System.out.println(addTest.add(num1,num2));//输入Int型,返回double

        float num3=3.0f;
        float num4=7.0f;
        System.out.println(addTest.add(num3,num4));//输入float型,返回double
    }


    public static void main(String[] args) {
        test();

    }
}

4.泛型的通配符和上限下限

泛型的通配符:
不知道使用什么类型类接收的时候,此时可以用 ? ,?号表示未知,通配符。此时只能接收数据,不能往集合中存储数据

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

/**
 * 测试泛型通配符
 * Created by cenyu on 16-11-22.
 */
public class GenericTypeDemo {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();
        doWork(list1);
        List<String> list2 = new ArrayList<>();
        doWork(list2);
        
    }
    
    //此处不知道会接收什么类型,用?通配符
    private static void doWork(List<?> list){
        
    }
}

上限和下限
用来限定的元素的类型必须是X类的子类或相同类,X的父类或相同类

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

/**
 * 测试泛型上下限
 * Created by cenyu on 16-11-22.
 */
public class GenericTypeDemo {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<Number> list3 = new ArrayList<>();
        List<Object> list4 = new ArrayList<>();
        
        doWork(list1);
//        doWork(list2);//此处报错
        doWork(list3);
//        doWork(list4);//此处报错
        
        
//        doWork2(list1);//此处报错
//        doWork2(list2);//此处报错
        doWork2(list3);
        doWork2(list4);

    }

    //泛型的上限:此时的泛型?,必须是Number类型或Number类的子类
    private static void doWork(List<? extends Number> list){

    }

    //泛型的下限:此时的泛型?,必须是Number类型或Number类的父类
    private static void doWork2(List<? super Number> list){

    }
}

5.泛型的删除和转换

泛型的擦除:
1.泛型编译之后就消失了(泛型自动擦除);
2.当把带有泛型的集合赋给不带泛型的集合,此持泛型被擦除(手动擦除)
示例:

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

/**
 * 测试泛型擦除
 */
public class GenericTypeDemo {
    public static void main(String[] args) {
        //带有Integer类型的泛型
        List<Integer> list1 = new ArrayList<>();
        list1.add(123);

        //不带泛型的集合
        List list2 = null;
        list2 = list1 //此时泛型被擦除
        list2.add("ABC"); //如果没被擦除是不允许用String的

        //带有String类型的泛型
        List<String> list3 = null;
        list3 = list2;//泛型被擦除了,然后又被赋值给list3.
                        //此时编译器不会报错,但是运行的时候还会报错
        //其实是等价于:String list3 = 123
        //此时用list3.get(0),是会报错的
    }

}


堆污染
上面的代码中,会有报错就是因为对污染。
单一一个方法中既使用泛型的时候也使用可变参数,此时容易导致堆污染问题

posted @ 2016-12-09 15:43  洋葱源码  阅读(302)  评论(0编辑  收藏  举报