所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值或参数的类型。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时)确定(即传入实际的类型参数,也称为类型实参)。
例如,集合类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对 象,所以在 JDK5.0 之前只能把元素类型设计为 Object,JDK5.0 时 Java 引入了 “参数化类型(Parameterized type)”的概念,允许我们在创建集合时指定集合元素的类型。比如:List
自定义泛型类或泛型接口
当我们在类或接口中定义某个成员时,该成员的相关类型是不确定的,而这个类型需要在使用这个类或接口时才可以确定,那么我们可以使用泛型类、泛型接口。
① 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型。
② 我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。
③ 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照 Object 处理,但不等价于 Object。
• 经验:泛型要使用一路都用。要不用,一路都不要用。
④ 泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。
⑤ 除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以 确定泛型结构中的泛型参数。 如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用 泛型参数。 我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。
class Person {
// 使用 T 类型定义变量
private T info;
// 使用 T 类型定义一般方法
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
// 使用 T 类型定义构造器
public Person() {
}
public Person(T info) {
this.info = info;
}
// static 的方法中不能声明泛型
//public static void show(T t) {
//
//}
// 不能在 try-catch 中使用泛型定义
//public void test() {
//try {
//
//} catch (MyException ex) {
//
//}
//}
}
自定义泛型方法
如果我们定义类、接口时没有使用,但是某个方法形参类型不确定时,这个方法可以单独定义。
• 泛型方法的格式:
[访问权限] <泛型> 返回值类型 方法名([泛型标识 参数名称]) [抛出的异常]{ }
• 方法,也可以被泛型化,与其所在的类是否是泛型类没有关系。
• 泛型方法中的泛型参数在方法被调用时确定。
• 泛型方法可以根据需要,声明为 static 的。
public class DAO {
public E get(int id, E e) {
E result = null;
return result;
}
}
泛型在继承上的体现
如果 B 是 A 的一个子类型(子类或者子接口),而 G 是具有泛型声明的类或接口,G并不是 G 的子类型!
当我们声明一个变量/形参时,这个变量/形参的类型是一个泛型类或泛型接口,例如:Comparator 类型,但是我们仍然无法确定这个泛型类或泛型接口的类型变量的具体类型,此时我们考虑使用类型通配符 ? 。
使用类型通配符:? 比如:List>,Map, ?>, List>是 List
写操作: 将任意元素加入到其中不是类型安全的:
Collection c = new ArrayListString>();
c.add(new Object()); // 编译时错误
因为我们不知道 c 的元素类型,我们不能向其中添加对象。add 方法有类型参数 E 作为集合的元素类型。我们传给 add 的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。 唯一可以插入的元素是 null,因为它是所有引用类型的默认值。
读操作: 另一方面,读取 List> 的对象 list 中的元素时,永远是安全的,因为不管 list 的真实类型是什么,它包含的都是 Object。
public class TestWildcard {
public static void m4(Collection coll){
for (Object o : coll) {
System.out.println(o);
}
}
}
有限制的通配符:
通配符指定上限: extends 类/接口A >
– 使用时指定的类型必须是继承某个类,或者实现某个接口,即 <=。(读数据可以,写数据除null外不行)
通配符指定下限: super 类/接口A >
– 使用时指定的类型必须是操作的类或接口,或者是操作的类的父类或接口的父接口,即>=。(读数据可以,写数据可以写入A及A的子类)
extends Number> //(无穷小 , Number]
//只允许泛型为 Number 及 Number 子类的引用调用
super Number> //[Number , 无穷大)
//只允许泛型为 Number 及 Number 父类的引用调用
extends Comparable>
//只允许泛型为实现 Comparable 接口的实现类的引用调用