泛型的官方定义
将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。结论就是让我们对于编码时,对于一些参数,我们正常都是指定类。
泛型的使用
定义只是告诉泛型是什么,有什么样的功效。但是在什么场景下,怎么样定义泛型来表达程序逻辑,却没有说明,这就需要我们自己去悟了。怎么去悟,我举例说明:
示例1
public class SingleResponse<T> extends Response {
private static final long serialVersionUID = 1L;
//动态数据内容
private T data;
}
上面代码定义的是一个返回的响应体。有过开发经验的都了解,每一个请求都会有不同的响应数据,但是我们是需要对这个响应需要有统一的规范或者监控管理,所以都期望所有的响应体都有相对统一的数据结构。这时候泛型就起到作用了。也就是把跟业务无关的数据定义一个类作为父类如下代码,那么继承这个父类之后,定义一个参数作为业务数据对象,也就是示例。这样也就是实现了结果数据结构统一。
public class Response extends DTO {
private static final long serialVersionUID = 1L;
private boolean success;
private String errCode;
private String errMessage;
}
这个时候请求-》响应这个过程中响应体就作为一个动态参数
示例2
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
这是一个函数接口,是jdk的开发人员定义的,我们可以尝试理解他们怎么去使用泛型的会有所启发。这个函数接口,有三个参数T,U,R。这个字母是非java的关键词,只是任意字母,不过在这个函数接口中T和U是方法入参,R代表返回结果。就相当于一个包工头雇了两个人干活,然后两个人把做出的东西呈现出来,再由包工头给承包方。
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
再看看这个同样的逻辑,只不过这个只有一个参数,他所表示的是包工头雇了一个人干活,活干完了,也不用汇报结果。
从示例可以看出这个泛型就是在对行为进行抽象后,跟行为相关的对象,全部用泛型形成参数,从而达到通用性,形成规范。
什么情况下使用泛型
泛型的使用就是保留扩展点。所谓的扩展点,就是我们所关注的点很可能不是固定,是可能变化的,但是又不可或缺的,可能新增,也可能修改,也可能删除等一系列操作。但是我们首先清楚第一个就是泛型描述的是一个类,根据我们常说的万物皆对象来理解,也就是泛型可以描述任何事物。我们来举例一个场景,我花了数根金条换了一些钱,然后到某个地方买了一辆车。我们要用泛型来定义这样一系列过程怎么定义呢?首先我们来找对象:
我: Person(P 人)
金条:Gold(G 金条)
钱: Money(M 钱)
地址: Address (A 地址)
车: Car (C 车)
那么形成的代码如下
/**
* 我:Person(P 人)
* 金条:Gold(G 金条)
* 钱:Money(M 钱)
* 地址: Address (A 地址)
* 车: Car (C 车)
*/
public interface BuyEvent<P,G,M,A,C>{
//取金条
G get(P person);
//换人民币
M exchange(G gold);
//到执行地址买车
C buyCar(M money,A address);
}
这样整个买车的人,车,钱,地址都是可以变的,也就是可以支持任何人用任何东西去任何地方买任何车。假如要扩展下,买的是奔驰车(类 BMCar),那么代码
/**
* 我:Person(P 人)
* 金条:Gold(G 金条)
* 钱:Money(M 钱)
* 地址: Address (A 地址)
* 车: BMCar (C 车)
*/
public interface BuyEvent<P,G,M,A,<C extends BMCar>>{
//取金条
G get(P person);
//换人民币
M exchange(G gold);
//到执行地址买车
C buyCar(M money,A address);
}
这是典型的所有的参数都转化对象,但是在编程中我们只需要对于那些有可能是变化的对象的出入参,进行保留扩展点可以定义成泛型参数,其他的可以定义成实体对象,也是需要考虑的,过于抽象也会增加编程的复杂度。这个度是需要我们仔细去斟酌的。
总结
泛型程序设计是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。使用泛型可以解耦事物的关联逻辑和对象关系,好的使用可以提高代码的阅读性,可维护性,从而提高代码生产力。