1.为什么使用泛型
比如:一个集合不指定类型你实际是想添加String类型,但是一不小心加了一个Integer类型进去,假如这时需要遍历取出所有数据,取出来的值你都赋给了一个string类型的变量,遍历到那个Integer数据时,程序会抛出ClassCastException的异常,这是运行时异常,如使用了泛型可以在编译器,在你添加那个Integer数据时就报错告诉你类型不对不可以往里面添加,可以让问题早点发现。
所以泛型的好处就是安全性和增加代码的可阅读性,还可以增加代码灵活性
// 泛型可以让代码更灵活
public void testSort() {
Integer[] arrayInt = new Integer[10];
sort(arrayInt);
Double[] doubles = new Double[10];
sort(doubles);
Object[] objects = new Object[10];
sort(objects);
}
public void sort(int[] array){}
public void sort(double[] array){}
// 使用泛型上面两个方法就可以不需要了
public <T> void sort(T[] array){}
Java泛型是jdk5新引入的特性,为了向下兼容,虚拟机其实是不支持泛型,所以Java实现的是伪泛型机制,也就是说Java在编译期擦除了所有的泛型信息,这样Java就不需要产生新的类型到字节码,所有的泛型类型最终都是一种原始类型,在Java运行时根本就不存在泛型信息
2.泛型方法、泛型类、泛型接口
- 泛型方法
public <T> AiPlate<T> getAiPlate() {
return new AiPlate<T>();
}
// 注意泛型方法的定义,要在返回值和修饰符中间加一个尖括号
- 泛型类
public class AiPlate<T> implements Plate<T> {
@Override
public void set(T t) {
}
@Override
public T get() {
return null;
}
}
- 泛型接口
public interface Plate<T> {
public void set(T t);
public T get();
}
3.泛型继承、泛型推断、通配符
- 泛型继承
// 定义类A1继承AiPlate类,AiPlate类不能跟类型型参
public class A1 extends AiPlate<T> {
}
// 如果想从AiPlate类派生一个子类,则可以改为如下代码
// 使用AiPlate类时为T形参传入String类型
public class A2 extends AiPlate<String> {
}
- 泛型推断
class MyUtil<E> {
public static <Z> MyUtil<Z> nil() {
return null;
}
public static <Z> MyUtil<Z> cons(Z head, MyUtil<Z> tail) {
return null;
}
E head() {
return null;
}
}
class Test {
public static void main(String[] args) {
// 通过方法赋值的目标参数来推断类型参数为String
MyUtil<String> ls = MyUtil.nil();
// 无须使用下面语句在调用nil()方法时指定类型参数的类型
MyUtil<String> mu = MyUtil.<String>nil();
// 可调用cons()方法所需的参数类型来推断类型参数为Integer
MyUtil.cons(42, MyUtil.nil());
// 无须使用下面语句在调用nil()方法时指定类型参数的类型
MyUtil.cons(42, MyUtil.<Integer>nil());
}
}
-
通配符
-
类型
?: 表示不确定的Java类型
T(type)表示具体的一个java类型
K V (key value)分别代表java键值对中的Key Value
E(element) 代表Element
-
4.泛型的类型消除、泛型约束
- 类型消除
public void testClass() {
ArrayList<Integer> integers = new ArrayList<>();
ArrayList<String> strings = new ArrayList<>();
System.out.println(integers.getClass() == strings.getClass());
}
// 通过运行发现两者是相等的,由此可见运行时类信息是完全一致的,泛型被擦除了只留下原始类型,
// 这里也就是arrayList
// 如果泛型类型没有限制,会直接擦除成object
public class AIPlate<T extends Comparable<T>> implements Plate<T> {
@Override
public void set(T t) {
}
@Override
public T get() {
return null;
}
}
// 如果有限制,AIPlate<T extends Comparable<T>> 像这样,字节码中会把T参数泛型擦除成第一个限制类型Comparable,
// 字节码中有桥方法指向实现的Plate.set方法,将set方法的泛型擦数成object
// 注意:虽然泛型被擦除了,其实在类的常量池里面保留了泛型信息
- 泛型约束
-
无界通配符 <?>
-
上界通配符 <? extends E>
static class A{} static class A1{} static interface B{} static interface B1{} // ok,继承多个类型时应该列出所有类型,并且有类型为类的必须写在接口的前面不然会有报错 static class C<T extends A & B & B1> {} // 报错,因为Java是单继承 static class C1<T extends A & A1 & B & B1> {}
-
下界通配符 <? super E>
-
?和T的区别
?和T都表示不确定的类型,区别在于T可以进行操作,但是?不可以例如:
T t = user; //可以
?u = user; //不可以
T是一个确定的类型,通常用于泛型类和泛型方法的定义,?是一个不确定的类型,通常用于泛型的调用代码和形参,不能用于定义类和泛型方法
-