泛型
泛型是Java SE5.0中新增内容,泛型很好的解决了杂乱的使用Object类型然后在强制类型转换的操作,提高代码的可读性(比如如果没有泛型,使用集合类时内部只能维护一个Object类数组,在获取值时还需强制转换为指定的类型),指定泛型后,可以在编译时期对类型进行检查(编译出错的类无法运行),避免使用错误类型的对象
// Java7 及以后省略构造后的泛型声明
ArrayList<String> objects = new ArrayList<>();
定义泛型
类型变量使用大写形式(小写也可用不推荐),且比较短,这是很常见的。在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型。T(需要时还可以用临近的字母U和S)表示“任意类型”
类
public class Person<T,U> {
private T name;
private U age;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public U getAge() {
return age;
}
public void setAge(U age) {
this.age = age;
}
}
Person<String, Integer> per = new Person<>();
per.setName("taoqz");
per.setAge(10);
String name = per.getName();
System.out.println(name);
一个类上可以声明多个多个泛型变量多个泛型变量使用逗号分隔,也可以限定泛型变量的类型,使用extends,多个限定使用&分隔,要注意类要在接口之前,并且限定类型中只能写一个类,因为只有java中只有单继承...,如果没有限定默认上限为Object
interface Demo1{
}
class Demo2 {
}
interface Demo3 {
}
// 编译出错,类的声明要在接口前
//class Demo<T1, T2 extends Integer, T3 extends Demo1 & Demo2> {
class Demo<T1, T2 extends Integer, T3 extends Demo2 & Demo1 & Demo3> {
}
方法
方法的泛型变量需要在方法的返回值之前定义使用<>包围,声明后的泛型变量可以在参数或返回值使用
// 没有返回值的写法
// public static <T> void getMiddle(T... a) {
public static <T> T getMiddle(T... a) {
return a[a.length / 2];
}
// String middle = MyTest.<String>getMiddle(arr);
// 可省略
String middle = getMiddle(arr);
System.out.println(middle);
类型变量的限定
有时需要对类,方法中的类型变量加以约束
// 泛型类需要继承或实现指定的类和接口,多个类或者接口时使用 & 分隔
public static <T extends Person & Comparable> T max(T[] a) {
// public static <T extends Person > T max(T[] a) {
// 使用该方法需要实现comparable接口
Arrays.sort(a);
return a[a.length-1];
}
@NoArgsConstructor
@AllArgsConstructor
public class Person<T,U> implements Comparable{
// 不能定义泛型静态域
// private static T name;
private T name;
private U age;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public U getAge() {
return age;
}
public void setAge(U age) {
this.age = age;
}
@Override
public int compareTo(Object o) {
return (int)((Person)o).getAge();
}
@Override
public String toString() {
return "Person{" +
"name=" + name +
", age=" + age +
'}';
}
}
@Test
public void demo2() {
Person<String, Integer> p1 = new Person<>("zs", 20);
Person<String, Integer> p2 = new Person<>("lisi", 18);
// 不能创建参数化类型的数组
// Person<String,Integer> pers = new Person<>[]; // 编译错误
Person[] people = {p1, p2};
Person max = max(people);
System.out.println(max);
}
注意事项
泛型擦除
泛型只在编译时期有效,在运行时期会被擦除(替换为Object或者有多个类型时使用第一个类型)
// 方法冲突,泛型擦除后,和Object中的equals无异
public boolean equals(T value){
return value.equals("123");
}
ArrayList<String> strings = new ArrayList<>();
strings.add("name");
// strings.add(123); // 编译错误
// 使用反射后可以添加任意类型
Method add = ArrayList.class.getMethod("add",Object.class);
Object invoke = add.invoke(strings, new Person<String,Integer>("zs",10));
System.out.println(invoke);
System.out.println(strings);
不能抛出或捕获泛型类的实例
// 不能继承异常相关类
//public class Emp<T> extends Exception {
//public class Emp<T> extends Throwable {
public class Emp<T> {
public <T extends Throwable> void fun(T t) {
try {
} catch (T e) { // 编译出错
}
}
public <T extends Throwable> void fun2(T t) throws Throwable {
try {
// 可以编译通过,合法
} catch (Throwable e) { // 编译出错
throw t;
}
}
}
不同泛型参数的泛型类
Person<String, Integer> p1 = new Person<>("zs",19);
// Person<Integer,String> p2 = p1; // 不同的泛型参数之前的类 没有关系
Person pp = p1;
Person<Integer,String> p2 = pp; // 可以正常运行
System.out.println(p2);
通配符
有时候我们需要限定泛型的类型在一个范围内,通配符可以解决这个问题
通配符无法在类上声明,主要在方法的参数或返回值上使用
public static void main(String[] args) {
// Pair<Emp> empPair = new Pair<>();
// Emp emp = new Emp();
// empPair.t = emp;
// emp.name = "taoqz";
// 这种情况是不能当参数的,因为Manager虽然是Emp的子类,但加泛型后便是两种没有任何关系的类型
Pair<Manager> empPair = new Pair<>();
fun(empPair);
}
public static void fun(Pair<Emp> empPair){
System.out.println(empPair.t.name);
}
public static void main(String[] args) {
// Pair<Manager> empPair = new Pair<>();
// Manager manager = new Manager();
// 也可写成多态的方式
Pair<Emp> empPair = new Pair<>();
Emp manager = new Manager();
manager.name = "zz";
empPair.t = manager;
fun(empPair);
}
// 此时可以借助通配符来限定类型 表示传入的类型必须是继承自Emp或者是Emp类 也就是限定了类型的最大范围
public static void fun(Pair<? extends Emp> empPair){
System.out.println(empPair.t.name);
}
// 表示必须是Emp类型 或者是Emp的父类,限定了类型的最小范围
public static void fun2(Pair<? super Emp> empPair){
System.out.println(empPair.t);
}
泛型与反射
待做....