Java从入门到放弃 · 深入泛型(一)

315 阅读3分钟

“这是我参与8月更文挑战的第20天,活动详情查看: 8月更文挑战

泛型

泛型的概念

泛型是一种未知的数据类型,当我们不知道使用什么数据类型时,就可以使用泛型。

泛型也可以看作是变量,用来接收数据类型。

我们常见的有

  • E e :Element 表示元素
  • T t:Type 表示类型

注意:当我们创建集合对象时,就会确定泛型的数据类型。

使用泛型的好处

我们知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升到object类型,当我们取出每一个对像,并且进行相应的操作时,必须采取类型转换。

通过下面的例子我们来体会下使用泛型的好处。

public static void main(String[] args) {
    //不使用泛型,这时集合默认的是object类型对象。
    ArrayList list = new ArrayList();
    list.add("abc");
    list.add(1);
    //遍历集合
    Iterator iterator = list.iterator();
    while(iterator.hasNext()){
        //此时正常打印
        Object next = iterator.next();
        System.out.println(next);
        //这时,我需要获取字符串的长度,使用String类的length方法
        //向下转型
        String s = (String)next;
        //这时会抛出ClassCastException类型转换异常。
        System.out.println(s.length());
    }
}

image.png 这里我们可以看出,不使用泛型不安全,容易引发异常。

那还有什么好处呢?

ArrayList<String> list = new ArrayList<>();
list.add("abc");
list.add(1);//error

使用泛型会把在运行期的错误在编译期就能检测出来。

那泛型就没有弊端吗?当然有。使用泛型时就只能在集合中存储同种类型的数据。

泛型的通配符

当使用泛型类或接口时,传递的数据中,泛型的类型不确定,此时可以通过通配符<?>表示,但是一旦使用通配符后,只能使用object类中的方法,集合元素自身方法无法使用。

通配符的基本使用

泛型的通配符:不知道使用什么类型来接收时,此时可以使用?,?表示位置通配符

注意:

  • 不能在创建对象时使用。
  • 只能作为方法的参数。 来看下面的例子,方便理解。
public static void main(String[] args) {
    ArrayList<String> list1 = new ArrayList<>();
    list1.add("abc");
    list1.add("def");
    ArrayList<Integer> list2 = new ArrayList<Integer>();
    list2.add(2);
    list2.add(3);

    /*
    我们要定义一个方法用来遍历任意的类型的集合,
    这时我们不知道集合都是什么类型的,那我们就可以使用通配符来作为方法的参数。
     */
    Print(list1);
    Print(list2);
}
private static void Print(Collection<?> list) {
    Iterator<?> iter = list.iterator();
    while(iter.hasNext()){
        Object next = iter.next();
        System.out.println(next);
    }
}

通配符的高级使用

在Java的泛型中可以指定一个泛型的上限和下限。

泛型的上限:

  • 格式:类型名称<? super 类> 对象名称
  • 意义:只能接收该类型及其父类。 泛型的下限:
  • 格式:类型名称<? extends 类> 对象名称
  • 意义:只能接收该类型及其子类。 同样我们来通过例子说明
public static void main(String[] args) {
    //这里有四个类:Integer Number String Object
    //Integer的父类时Number,NUmber的父类是Object
    //String类的父类是Object
    ArrayList<Integer> list1 = new ArrayList<>();
    ArrayList<String> list2 = new ArrayList<>();
    ArrayList<Number> list3 = new ArrayList<>();
    ArrayList<Object> list4 = new ArrayList<>();

    getElement1(list1);//error
    getElement1(list2);//error
    getElement1(list3);
    getElement1(list4);
    
    getElement2(list1);
    getElement2(list2);//error
    getElement2(list3);
    getElement2(list4);//error
}

private static void getElement1(Collection<? super Number> list) {
}
private static void getElement2(Collection<? extends Number> list) {
}

我们看到有的报错,有的不报错,这就是收到了泛型上限和下限的作用。

写在最后

好了,这里带大家了解了泛型,以及泛型的好处和弊端,以及泛型通配符的使用。下篇我们继续深入学习泛型的使用。

以上内容如有不正之处,欢迎掘友们批评指正。