泛型(Generic):字面意思就是泛化类型。其实就是”参数化类型“。就是将原来具体的数据类型泛化为一个在编译时暂时不确定的类型。等到使用时才确定具体的数据类型。
1、是什么?
所谓泛型就是在定义类、接口是通过一个标识(菱形语法“<>”)来表示类中某个属性的类型或者是某个方法的返回值或方法的参数类型。这个类型参数将在使用时确定。
2、为什么?
(1)解决元素存储的安全性问题。
a. 未定义泛型时:
List list = new ArrayList();
list.add(111);
list.add(222);
//未定义泛型时,可以存放任意类型的数据,类型不安全
list.add("hello");
b. 定义泛型时:
List<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
//如果定义了泛型,在编译时,类型检查就会不通过,保证数据的安全
//list.add("hello");
(2)解决获取数据时,需要类型强制转换的问题。
a. 未定义泛型时:
List list = new ArrayList();
list.add(111);
list.add(222);
//未定义泛型,遍历时需要进行强制类型转换,可能会出现ClassCastException
for (Object obj : list) {
int i = (int) obj;
System.out.println(i);
}
b. 定义泛型时:
List<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
//定义了泛型,可以避免强制类型转换
for (Integer i : list) {
System.out.println(i);
}
3、怎么用?
常用的泛型结构有3种:泛型类、泛型接口、泛型方法。下面通过参考JDK中的 List 和 ArrayList 来说明这三种结构。当然我们也可以自定义泛型。这里只是简单的抽取其中部分代码来说明问题。
(1)泛型类
//在类或接口上声明的泛型,在本类或本接口中即代表某种类型,
public class ArrayList<E> {
//可以作为非静态属性的类型
private E[] elementData;
//非静态方法的参数类型或返回值类型, 但在静态方法中不能直接使用类或接口的泛型。
public E get(int index) {}
public boolean add(E e) {}
}
(2)泛型接口
//和泛型类类似
public interface List<E> {
public E get(int index) {}
public boolean add(E e) {}
}
(3)泛型方法
// 泛型方法的格式:
// [访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
// 非静态泛型方法
public <T> T[] toArray(T[] a) {
...
}
// 静态泛型方法
public static <T> T staticMethod(T t) {
System.out.println("staticMethod..." + t);
return null;
}
(4)泛型的继承
a. 如果子类不确定父类中的泛型类型,可以继承:
public class ArrayList<E> extends List<E> {
}
b. 如果可以确定则可以直接指明父类的泛型类型:
public class ArrayList extends List<Integer> {
}
c. 子类还可以再重新定义自己新的泛型:
public class ArrayList<S> extends List<Integer> {
private S s;
public void show(S s) {}
}
(5)类型通配符 “?”
通配符 “?” 的类型类似于 Object 类型,但不真的是 Object 类型,它可以看成是任何类型的父类。
a. 泛型是 "?"。
<?> : 可以接收任何类型
b. 上界
<? extends A> : 可以接收A类型及其所有的子类类型(可以看做是 <=A)
c. 下界
<? super A>: 可以接收A类型及其所有的父类类型(可以看做是 >=A)
(6)多个泛型
当使用多个泛型参数时,可以使用“,” 分隔。可以参考 JDK 中的 Map<K, V> 或 HashMap<K, V> 来使用。
注意:
- 泛型不同的引用不能相互赋值;
- 尽管在编译时ArrayList<String>和ArrayList<Integer>是两种类型,但是,在运行时只有 一个ArrayList被加载到JVM中
- 异常类不能是泛型的;
- 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity]; 参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
总结:不论是泛型类、泛型接口还是泛型方法都必须先声明再使用。