泛型(generics)
前言
出现的原因:想让一个类可以用于所有类型,但是我们在以前定义的时候,只能定义一种类型,于是就出现了泛型。
泛型使用
我们可以看看 Java 定义的 List 接口的使用。
import java.util.ArrayList;
import java.util.List;
public class GenericTest {
public static void main(String[] args) {
// 指定类型 ,list 只能存储 String 元素,告诉 Java 这个 List 只能有 String 元素
List strList = new ArrayList<>();
strList.add("str1");
strList.add("str2");
strList.add("str3");
strList.forEach(System.out::println);
}
}
上面的 List 的引用指向的是 ArrayList 对象,我们看一下 ArrayList 中的 add 方法定义。
看一下 ArrayList 的 add 方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
可以看到要添加的元素类型是 E,那么这个 E 来自哪里呢?我们看一下 ArrayList 的定义:
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
我们创建对象时是这样的:
List strList = new ArrayList<>();
也就是说,这个 E 就是我们在创建 ArrayList 对象时放入的类型,通过看 add 方法我们发现,要添加的元素也必须是这个类型。
当然别的类不一定是这样,要根据泛型的定义来判断使用的方式,具体情况,具体分析。
如何定义一个泛型
将类型定义在类后面的尖括号 ,可以定义多个。
public class MyGenericType1 {
private MyType1 type1;
private MyType2 type2;
}
其实泛型就是类型擦除,然后在调用的时候,帮我们做强制类型转换。如果出现错误,报错信息会显示在调用的那一行。
协变和逆变
协变和逆变都是针对引用类型的,不能用于创建对象类型,在创建对象时必须指定一个明确类型。
协变
List extends Person> persons = null;
这种形式就是协变,使用协变时,类必须是 ? extends 后面的类本身或其子类。
逆变
List super Student> person2s = null;
这种形式就是逆变,使用逆变时,类必须是? super 后面的类本身或其父类。
import java.util.ArrayList;
import java.util.List;
public class GenericTest {
public static void main(String[] args) {
// 指定类型 ,list 只能存储 String 元素,告诉 Java 这个 List 只能有 String 元素
// 协变:类必须是 ? extends 后面的类本身或其子类
List extends Person> persons = null;
// 不可以加入(List并没有记住是什么类型,需要指定明确的类型)
// persons.add(new Person());
// persons.add(new Student());
persons = new ArrayList();
persons = new ArrayList();
// 逆变:类必须是 ? super 后面的类本身或者其父类类
List super Student> person2s = null;
person2s = new ArrayList();
person2s = new ArrayList();
}
}
写入使用逆变,读取使用协变。可以参考 Java 类库中的方法。
泛型总结
泛型最重要的就是这两点。
编译器检查并进行类型擦除。
使用时会进行类型强制转换。