3

Java 中一个天天都在被人使用,你一定听过,但是并不知道为什么的知识点

 2 years ago
source link: http://www.justdojava.com/2021/10/31/fanxing/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java 中一个天天都在被人使用,你一定听过,但是并不知道为什么的知识点

发表于 2021-10-31

| 分类于 Java

泛型作为 Java 中一个天天都在被人使用的特性,你真的知道它的原理吗?

首先我们说下什么是泛型。

泛型,就是泛化类型也就是泛化参数类型。平时我们在编写代码的时候,方法的参数在定义的时候都是指定特定的类型,比如 IntegerDouble 或者其他自己别笑的类。那么泛化类型就是,我们在编写一个方法的时候对于参数的类型不具体指定,而是定义一个通用类型,在使用的时候根据类型自动转化。

上面的描述可能比较抽象,我们再看一下,如果没有泛型的话,会出现什么情况以及为什么说这个泛型大家天天都在使用。

我们都知道 ArrayList 作为 Java 中一个很频繁被使用的集合,它是一个可变长的数组,底层是基于 Object[] 来实现的。

01.png

可以简单理解为下面的内容

public class ArrayList {
    private Object[] array;
    private int size;
    public void add(Object e) {...}
    public void remove(int index) {...}
    public Object get(int index) {...}
}

如果说这个时候我们使用上面的 ArrayList 去存储 String 类型的话,需要如下操作,在使用的时候必须进行手动强转。

ArrayList list = new ArrayList();
list.add("Java");
list.add("C++");
String first = (String) list.get(0);
String first = (String) list.get(1);

首先看到上面的代码,大家一定会诧异,要是每次使用的使用都这样显示的强转的话,那不是要命了么,而且这还是使用者知道是什么类型的情况才能进行手动强转,如果说根本不知道是什么类型的时候,根据没办法进行强转,这种方式简直不能忍,还特别容易出错。

那怎么解决这个问题呢?有朋友说我们可以对于不同的类型实现一个自己的 ArrayList 类,这样在使用的时候就可以不用强转了啊。对此阿粉只能说,对于 JDK 提供的类可以这样做,但是对于用户自己编写的类怎么实现呢?

这个时候大家可能会说到,ArrayList 我天天使用,也没手动强转过啊,不还是用的好好的。

这就要归功于我们今天所说的主角,泛型了。

我们给 ArrayList 增加的泛型,通过定义一个泛化的类型,当我们在使用的时候如果传递的类型不是指定的类型,那么在编译的阶段就会报错,从而也就不会有需要强转的操作了。

public class ArrayList<E> {
    private Object[] array;//任何类型都是 Object 的子类,所以这里我们还是不变
    private int size;
    public void add(E e) {...}
    public void remove(int index) {...}
    public E get(int index) {...}
}

这样修改过后,我们在编写代码的时候就可以如果进行

ArrayList<String> strList = new ArrayList<String>();
list.add("Java");
list.add("C++");
String first = list.get(0);//这里就不用强转了
String first = list.get(1);//这里就不用强转了
list.add(new Integer(100));//编译报错

当我们需要使用 Integer 对象的时候就可以使用下面这种方式

ArrayList<Integer> list = new ArrayList<Integer>();
list.add("Java");//编译报错
list.add("C++");//编译报错
list.add(new Integer(100));//编译通过

另外我们还知道 ArrayList 实现了 List 接口,如下所示,所以会有一种向上转型的概念,就是我们前面在定义的时候使用 List 也是可以,也就是我们通常的定义方式,即 List<String> list = new ArrayList<>();

02.png

但是这里我们需要注意不可以泛型进行向上转型,比如下面这个例子。

我们定义了 Person 类,Man 类以及 Women

public class Person {

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

public class Man extends Person {

    ...
}

public class Women extends Person {

    ...
}

我们在使用的时候只能这样

ArrayList<Man> manList = new ArrayList<Man>();
List<Man> manList1 = new ArrayList<>();
ArrayList<Women> womenList = new ArrayList<Women>();
List<Women> womenList1 = new ArrayList<>();
ArrayList<Man> manList = new ArrayList<Man>();
//这种转型是不可以的
ArrayList<Person> personList = manList;
personList.add(new Man());
//破坏了原本只能存放 Man 的约定
personList.add(new Women());

接下来我们再看另一个问题,假设我们有一个方法,是打印 PersonList 内容的,如下所示:

public void print(ArrayList<Person> personList) {
	for (Person p : personList) {
		System.out.print(p.name);
	}
}
ArrayList<Man> manList = new ArrayList();
list.add(new Man());
list.add(new Man());
print(manList);

上面的内容会编译出错,效果是这样的。

03.png

原因是因为虽然 Man 类是继承了 Person 类,但是 ArrayList<Man> 并没有继承 ArrayList<Persion> 类,所以这个方法是编译通不过。看到这里小伙伴又惊呆了,这不行啊,总不能引入了泛型,就不支持多态了吧,所以这个要怎么办。

这里我们就需要引入另一个东西了,那就是泛型里面的 extends,我们把 print 方法换个写法,这个时候就不会编译不通过了。extends 表示传进来的参数只要是 Person 的子类都可以,这样就还支持多态了。所以现在小伙伴知道了为什么JDK 源码以及很多框架的源码中都有很多? extends xxx 这种形式的代码了吧。

04.png

  1. 廖雪峰- java 教程

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK