泛型简介
泛型的意义在于代码复用,简化多种数据类型执行的相同的代码。
泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
引入泛型,它将提供类型的约束,提供编译前的检查:
List<String> list = new ArrayList<String>();
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法。
泛型的使用
泛型类
一元泛型类:
class Point<T>{ // 此处可以随便写标识符号,T是type的简称
private T var;
public T getVar(){
return var;
}
public void setVar(T var){
this.var = var;
}
}
多元泛型类:
class func<K,V>{ // 此处指定了两个泛型类型
private K key;
private V value;
public K getKey(){
return this.key;
}
public V getValue(){
return this.value;
}
public void setKey(K key){
this.key = key;
}
public void setValue(V value){
this.value = value;
}
}
泛型接口
interface Info<T>{ // 在接口上定义泛型
public T getVar(); // 定义抽象方法,抽象方法的返回值就是泛型类型
}
class InfoImpl<T> implements Info<T>{
private T var;
public InfoImpl(T var){ // 通过构造方法设置属性内容
this.setVar(var);
}
public void setVar(T var){
this.var = var;
}
public T getVar(){
return this.var;
}
}
泛型方法
泛型方法的定义:
/**
* 泛型方法
* @param c 用来创建泛型对象(本身是一个Class对象)
* @param <T> 声明一个泛型
* @return
*/
public<T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException {
T t = c.newInstance();
return t;
}
newInstance()方法无法提供参数,因此,使用该泛型方法的类需要提供一个参数列表为空的构造方法。
泛型方法的返回值未必一定该泛型,但是需要在返回值前添加<T>,来声明这是一个泛型方法。
泛型方法的意义在于可以在调用的时候指明类型,更加灵活。泛型类要在实例化的时候就指明类型,如果想换一种类型,就需要重新new一次。
泛型方法的调用:
Generic generic = new Generic();
Object object = generic.getObject(Class.forName("User"));
forName()方法中传入实体类的完整路径,通过泛型方法生成该类对象。
泛型的边界
假设我们有:
class A{}
class B extends A {}
class C extends String{}
这是一个错误的代码段:
public static void funA(List<A> listA) {
// ...
}
public static void funB(List<B> listB) {
funA(listB);
}
为了解决泛型中隐含的转换问题,Java泛型加入了类型参数的上下边界机制。
<? extends A>
表示该类型参数可以是A(上边界)或者A的子类类型。编译时擦除到类型A,即用A类型代替类型参数。如果传入的实例类型B是在这个范围内的话允许转换,运行时会把对象当做A的实例看待。
同理,<? super A>
表示该类型参数可以是A(下边界)或者A的父类类型。
public static void funA1(List<? extends A> listA) {
System.out.println("funA1");
}
public static void funA2(List<? super A> listA) {
System.out.println("funA2");
}
public static void funB(List<B> listB,List<Object> listO) {
funA1(listB);
funA2(listO);
}
public static void main(String[] args) {
List<B> listB = new ArrayList<>();
List<Object> listO = new ArrayList<>();
funB(listB,listO);
}
我们也可以用这种方式定义泛型类的边界:
class number<T extends Number>{ // 此处泛型只能是数字类型
private T var;
public T getVar(){
return var;
}
public void setVar(T var){
this.var = var;
}
}
我们可以使用 & 符号声明更多的限制:
//工资低于2500元的上斑族并且站立的乘客车票打8折
public static <T extends Staff & Passenger> void discount(T t){
}
为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符,使用的规则是:生产者有上限、消费者有下限
- 如果参数化类型表示一个 T 的生产者,使用 < ? extends T>;
- 如果它表示一个 T 的消费者,就使用 < ? super T>;
- 如果既是生产又是消费,那使用通配符就没什么意义了,因为你需要的是精确的参数类型。
泛型数组
List<String>[] list1 = new ArrayList<String>[10]; //编译错误,非法创建
List<String>[] list1 = new ArrayList<?>[10]; //编译错误,需要强转类型
List<String>[] list1 = (List<String>[]) new ArrayList<?>[10]; //OK,但是会有警告
List<?>[] list1 = new ArrayList<String>[10]; //编译错误,非法创建
List<?>[] list1 = new ArrayList<?>[10]; //OK
List<String>[] list6 = new ArrayList[10]; //OK,但是会有警告
合理的使用:
public <T> void ArrayWithTypeToken(Class<T> type, int size) {
T[] array = (T[]) Array.newInstance(type, size);
}