Java学习笔记(六):集合框架

集合框架

泛型

为什么需要使用泛型:

1.存储在任意类型的数据在集合中,取出来都是Object类型的,此时就要强制转换

public static void main(String[] args) {
    List list = new ArrayList();
    list.add(1);
    Object ele = list.get(0);
    Integer num = (Integer) ele;
    System.out.println(num);
}

2.约束储存到集合中的元素必须是相同的数据类型(相同数据类型才能作比较,比如TreeSet类)

TreeSet set = new TreeSet();
    set.add(123);
    set.add("ABC");//报错:ClassCastException

3.设计一个点类(point),来封装坐标位置,要求坐标支持String类型,Integer类型和Double类型

如果不使用泛型,将像下面这样定义:

class Point1{
    private String x;
    private String y;
}
class Point2{
    private Integer x;
    private Integer y;
}
class Point3{
    private Double x;
    private Double y;
}

这样重复了代码,使得代码非常不优雅。

泛型(GenericType):Java5开始支持的新的语法

1.广泛通用的类型;
2.代码模板中类型不确定,谁调用这段代码,就是什么类型;

Point.java

//Type(T);Element(E);Key(K);Value(V)
public class Point <T> {
    private T x;
    private T y;

    public T getX() {
        return x;
    }
    public void setX(T x) {
        this.x = x;
    }
    public T getY() {
        return y;
    }
    public void setY(T y) {
        this.y = y;
    }
}

PointDemo.java

public class PointDemo {
    public static void main(String[] args) {
        //使用String类型
        Point<String> p1 = new Point<String>();
        String x1 = p1.getX();
        //使用Integer类型
        Point<Integer> p2 = new Point<Integer>();
        Integer x2 = p2.getX();
        //使用Double类型
        Point<Double> p3 = new Point<Double>();
        Double x3 = p3.getX();
    }
}

我们使用<>来传入泛型的类型,如:List<String> list = new List<String>();前后<>中的内容必须一样,因此不存在泛型继承的概念。
但是后面的<>内容可以省略,如List<String> list = new List<>();
我们从反编译代码中可以看出:
反编译泛型
其实泛型也是一种语法糖!

泛型方法

情况一:泛型类中的泛型只能适用于非静态方法,如果需要给静态方法设置泛型,此时应该使用泛型方法;
情况二:泛型类中的泛型应该适用与整个类中多个方法,有时只对某一个方法设置泛型即可。

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

泛型方法具体展示:

public static <T> T doWrok(T val) {
    return val;
}

泛型的通配符以及上限和下限

泛型的通配符:
不知道使用什么类型来接收的时候,此时可以使用,表示未知。
此时只能接收数据,不能声明和存储数据。

public static void main(String[] args) {
    List<Integer> list1 = new ArrayList<>();//这里不能用<?>来声明
    doWork(list1);
    List<String> list2 = new ArrayList<>();
    doWork(list2);
}

public static void doWork(List<?> list){

}

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

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<>();

        doWork1(list1);//true
        doWork1(list2);//false
        doWork1(list3);//true
        doWork1(list4);//false

        doWork2(list1);//false
        doWork2(list2);//false
        doWork2(list3);//true
        doWork2(list4);//true
    }
    //泛型的上限:此时的泛型?必须是Number类或者是Number类的子类
    public static void doWork1(List<? extends Number> list){

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

    }
}

泛型的擦除和转换

泛型的擦除:

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

public static void main(String[] args) {
    List<Integer> list1 = new ArrayList<>();
    list1.add(123);

    List list2 = null;
    list2 = list1;// 此时泛型擦除
    list2.add("ABC");

    // 带有String类型的泛型
    List<String> list3 = null;
    list3 = list2;
    System.out.println(list3);

    //等价于String num2 = 123 报错
    String num = list3.get(0);
}

堆污染:
当一个方法既使用泛型,又使用可变参数的时候,此时容易导致堆污染问题。

//Type safety: Potential heap pollution via varargs parameter arr
    @SafeVarargs //抑制警告
    public static <T>List<T> asList(T... arr){
        return null;
    }

泛型详解,参考https://www.cnblogs.com/coprince/p/8603492.html

猜你喜欢

转载自blog.csdn.net/qq_42650988/article/details/81811534