Java泛型
一、什么是泛型?
Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.
泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型
参数化类型:
- 把类型当作是参数一样传递
<数据类型>
只能是引用类型
相关术语:
-
ArrayList<E>
中的E称为类型参数变量 -
ArrayList<Integer>
中的Integer称为实际类型参数 - 整个称为
ArrayList<E>
泛型类型 - 整个
ArrayList<Integer>
称为参数化的类型ParameterizedType - 作用:使用多种不同类型的对象重复使用。类型复用。
- 为什么要用泛型?使用继承,使用Object类同样可以实现类型复用,但是Object是所有类的父类,一但遇到向下转型进行强制类型转换,这样很容易产生错误,还有就是使用对象带有Object参数,对象类型的值不明确,容易出错。(不熟悉向下转型可以看看多态)。 泛型使用的是类型参数,不需要类型转换,编译器会检查对象的类型。
- Collection、Map集合对元素的类型是没有任何限制的。本来我的Collection集合装载的是全部的Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的。
- 把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object。外边获取该对象,还需要强制转换
有了泛型以后:
- 代码更加简洁【不用强制转换】
- 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
- 可读性和稳定性【在编写集合的时候,就限定了类型】
public class Gen{ private Object one; private Object two; public Gen(){ super();// 默认调用Object构造器 } public Gen(Object one,Object two){ this.one = one;this.two=two; } /** * 省略 get set */ } public static void main(String[] args) { Gen gen0 = new Gen(); Object gen1 = new Gen(); Gen gen2 = (Gen)new Object(); }
public class Gen<T>{ private T one; private T two; public Gen(){ super();// 默认调用Object构造器 } public Gen(T one,T two){ this.one = one;this.two=two; } /** * 省略 get set */ } public static void main(String[] args) { Gen<String> stringGen = new Gen<>(); Object o = new Gen<>(); Gen gen = new Gen(); }
补充:
泛型的上限:
- 格式:
类型名称 <? extends 类 > 对象名称
- 意义:
只能接收该类型及其子类
泛型的下限:
-
格式:
类型名称 <? super 类 > 对象名称
-
意义: 只能接收该类型及其父类型
<T extends BoundingType> // T 是 限定类型(BoundingType)的 子类型(SubType) // T 和 限定类型(BoundingType)可以是类也可以是接口 // T extends Comparable & Serializable 限定类型用"&" 分隔,T 类型变量用","分隔 // 最多有一个限定类型是类,且必须是第一个限定
类型擦除
- 定义一个泛型类型,都会提供一个相应的原始类型(raw type)
- 原始类型名就是去掉类型参数后的泛型类型名
- 类型变量会被擦除(erased),并替换为其限定类型,无限定类型替换为Object
限制与局限性
-
不能用基本类型实例化类型参数
-
运行时查询只适用于原始类型
-
不能创建参数化类型数组
new Object<String>[10]; // error
-
不能实例化类型变量
T t = new T(); //error
-
不能构造泛型数组
T[] t = new T[10]; //error
-
泛型类的静态上下文中引用类型变量无效,不能在静态字段和静态方法中引用类型变量
static T t; //error static T hello(){}; // error
-
不能抛出或捕获泛型类的实例,catch语句中不能引用类型变量
try{...}catch(T e){} // error
-
可以取消对检查型异常的检查,使用注解 @SuppressWarning(“unchecked”) 设置为非检查型异常
-
擦除后的冲突
1,当泛型类型被擦除后,不允许创建引发冲突的条件.
// 冲突 public class OBJ<T>{ public boolean equals (T t){...} } public class OBJ<String>{ public boolean equals (String str){...} public boolean equals (Object obj){...} } // 解决办法 重命名冲突方法
2,限制: 一个类或是类型变量不能同时作为两个接口是同一接口的不同参数化的子类
泛型继承
public class Person<T extends Employee>{} /** * 类的继承 */ class Employee{} // 父类 class Manager extends Employee{} // 子类 /** * 泛型 中 <> 没有任何关系 * 类型安全 */ class Person<Employee>{} class Person<Manager>{} /** * 泛型的继承 * ArrayList<T>类 实现了 List<T>接口 * ArrayList<Employee> 可以转化成 List<Employee> */ interface List<Employee>{} class ArrayList<Employee> implement List<Employee>{}
通配符类型
-
通配符概念
// ? 允许参数类型变化 Person<? extends Employee> p; // 变量 public class Employee { @Override public String toString() { return "Employee"; } } public class Manager extends Employee{ @Override public String toString() { return "Manager"; } } public class Person<T extends Employee>{ // 需要 Employee 某个子类型 public static void main(String[] args) { Person<? extends Employee> p1 = new Person<Employee>(); Person<? extends Employee> p2 = new Person<Manager>(); // 参数类型变化 System.out.println(p1.toString()); System.out.println(p2.toString()); Person<Employee> p3 = new Person<Employee>(); // Person<Employee> p4 = new Person<Manager>(); // 参数类型不同 System.out.println(p3.toString()); } @Override public String toString() { return "Person"; } }
-
通配符的超类型限定
/** * 超类型限定 (supertype bound) * 类型名称 <? super 类 > 对象名称 * 只能接收该类型及其父类型 **/ Admin<? super Manager> a; public class Admin<T extends Manager>{ public static void main(String[] args) { Admin<? super Manager> a = new Admin<Manager>(); Admin<? super Manager> a2 = new Admin<>(); System.out.println(a.toString()); System.out.println(a.toString()); //Admin<? super Manager> a1 = new Admin<Employee>(); set(a); } private static void set(Admin<? super Manager> admin){ // 使用超类型限定通配符的方法不能有返回值 System.out.println(admin); } @Override public String toString() { return "admin"; } }
-
无限定通配符
// One<?> o; public class One<T>{ public static void main(String[] args) { System.out.println(getInstance()); System.out.println(get(new One<>())); } public static One<?> getInstance(){ // 返回值只能赋值给Object return new One<String>(); } public static String get(One<?> one){ //不需要实际的类型 return one.toString(); } @Override public String toString() { return "one"; } }
-
通配符捕获
public class BuHuo { public static void main(String[] args) { Po<Object> po = new Po<>(); System.out.println(po.getFirst(po.getSecond())); swap(po); } static class Po<T>{ public Po<T> getFirst(Po<?> po) { return new Po<T>(); } public Po<T> getSecond(){ return new Po<>(); } @Override public String toString() { return "innerClassPo"; } } static void swap(Po<?> p){ // ? t = p.getFirst();//error p.getFirst(p.getSecond()); } static <T> void swapHelper(Po<T> p){ // T 指向那个类型 Po<T> t = p.getFirst(p.getSecond()); Po<T> t1 = p.getSecond(); p.getFirst(t); p.getFirst(t1); } }
-
.