1 泛型是什么
让一个类 / 方法,能够支持多种不同类型的数据。这样就不用一遍又一遍的写,很简洁方便,一个代码可以使用多次。
2 使用 Object
使用 Object 来完成 “ 泛型 ”效果,是比较麻烦的。一方卖弄,需要写一些类型转换的代码;另一方面,类型转换的代码容易出错,缺少一些必要的类型检查。
public class MyArray {
private Object[] data = null;
private int size = 0;
private int capacity = 10;
public MyArray() {
data = new Object[capacity];
}
public void add(Object data){
if (size >= capacity){
return;
}
this.data[size++] = data;
}
public Object get(int index){
return data[index];
}
public static void main(String[] args) {
MyArray myArray = new MyArray();
myArray.add(new Person("奈奈生",18));
myArray.add(new Person("鞍马",19));
myArray.add(new Person("瑞希",19));
Person person = (Person) myArray.get(0);
MyArray myArray2 = new MyArray();
myArray2.add("hello");
myArray2.add("java");
String str = (String) myArray2.get(0);
MyArray myArray3 = new MyArray();
myArray3.add(1);
Integer integer = (Integer)myArray3.get(0);
}
}
public class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
3 使用泛型
泛型包括:泛型类、泛型方法、泛型接口。
类名后面加上 泛型参数,<> 表示当前这个类是个泛型类,E相当于是一个形参,表示某一具体的类型,会在实例化的时候具体确定是什么类型。
public class MyArray2<E> {
private E[] data = null;
private int size = 0;
private int capacity = 10;
public MyArray2() {
// 错误
// data = new E[capacity];
data = (E[])new Object[capacity];
}
public void add(E data){
if (size >= capacity){
return;
}
this.data[size++] = data;
}
public E get(int index){
return this.data[index];
}
public static void main(String[] args) {
MyArray2<String> myArray2 = new MyArray2<>();
myArray2.add("hello");
String str = myArray2.get(0);
MyArray2<Integer> myArray3 = new MyArray2<>();
myArray3.add(1);
myArray3.add(2);
Integer ret = myArray3.get(0);
}
}
使用泛型之后,在针对对象实例化的时候,需要填写泛型参数的实际类型,然后所有的 泛型参数 E 就都被替换成我们实例化时填进去的类型了。
本质上还是一个 Object[] ,只是帮我们完成了类型校验 和 类型转化。
4 类型边界
定义泛型类的时候,对未来实例化时,传入的参数的类型做出限制,类型不能随便写,做出一些约束和限制。
public class MyArray2<E extends Animal> {
private E[] data = null;
private int size = 0;
private int capacity = 10;
public MyArray2() {
// 错误
// data = new E[capacity];
data.eat();
data = (E[])new Object[capacity];
}
public void add(E data){
if (size >= capacity){
return;
}
this.data[size++] = data;
}
public E get(int index){
return this.data[index];
}
public static void main(String[] args) {
// String 错误,因为 String 不是 Animal 的子类
// MyArray2<String> myArray2 = new MyArray2<>();
// myArray2.add("hello");
// String str = myArray2.get(0);
// Integer 错误,因为 Integer 不是 Animal 的子类
// MyArray2<Integer> myArray3 = new MyArray2<>();
// myArray3.add(1);
// myArray3.add(2);
// Integer ret = myArray3.get(0);
// 正确
MyArray2<Animal> myArray2 = new MyArray2<>();
MyArray2<Cat> myArray21 = new MyArray2<>();
}
}
public class Animal {
}
public class Cat extends Animal{
}
泛型边界是在创建时设计的。
5 通配符
也是限制泛型参数传入的条件,是在泛型类使用时涉及的,尤其是泛型类作为某个方法的参数时。
// 创建
public class MyArray<E> {
...
}
// 调用
public static void printAll(MyArray<?> m)
// 此处的 ? 就是通配符
// 意思是 调用printAll时,参数类型填什么都可以
// 以下调用都是正确的
printAll(new MyArray<String>());
printAll(new MyArray<Integer>());
printAll(new MyArray<Double>());
printAll(new MyArray<Number>());
printAll(new MyArray<Object>());
E 只能是在创建的时候使用的,在调用的时候不能实用 E ,用 “ ? ”。
5.1 通配符上界
// 传入类型实参是 Animal 子类的任意类型的 MyArray
public static void printAll(MyArray<? extends Animal> arr) {
...
}
// 以下调用都是正确的
printAll(new MyArray<Cat>());
printAll(new MyArray<Animal>());
// 以下调用是编译错误的
printAll(new MyArray<String>());
printAll(new MyArray<Object>());
5.2 通配符下界
// 可以传入类型实参是 Cat 父类的任意类型的 MyArray
public static void printAll(MyArray<? super Animal> arr) {
...
}
// 以下调用都是正确的
printAll(new MyArrayList<Animal>());
printAll(new MyArrayList<Object>());
// 以下调用是编译错误的
printAll(new MyArrayList<String>());
printAll(new MyArrayList<Cat>());
5.3 泛型中的父子关系
public class MyArray<E> { ... }
// MyArray<Object> 不是 MyArray<Animal> 的父类型
// MyArray<Animal> 也不是 MyArray<Cat> 的父类型
// 需要使用通配符来确定父子类型
// MyArray<?> 是 MyArray<? extends Animal> 的父类型
// MyArray<? extends Animal> 是 MyArrayList<Dog> 的父类型