泛型的高级运用,代码重构必须要了解的技巧

简介: 前言泛型相信大家都不陌生,经常都会用到,像在一些集合类啊,一些开源框架啊,这种东西随处可见,如果不能好好理解的话,看起源码来也会增加了一点儿复杂度。泛型的好处,扩展性强,低耦合业务内容,大幅度的减少重复代码。本篇文章,基于对泛型有一定了解,想更进一步运用的朋友。

前言

泛型相信大家都不陌生,经常都会用到,像在一些集合类啊,一些开源框架啊,这种东西随处可见,如果不能好好理解的话,看起源码来也会增加了一点儿复杂度。

泛型的好处,扩展性强,低耦合业务内容,大幅度的减少重复代码。

本篇文章,基于对泛型有一定了解,想更进一步运用的朋友。

泛型的运用

场景一

当我们写了一个采用泛型的类,但是怎么获取到这个类上的泛型呢,直接 run 一段简短的代码看下。

/**
 * @author: wangqp
 * @create: 2020-11-18 15:02
 */
public class GenericApply<T,U> {
    public T apply(T t){
        return t;
    }
    public List<String> getGenericClassName(){
        List<String> ret = new ArrayList<>();
        Type genericSuperclass = getClass().getGenericSuperclass();
        Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
        Stream.of(actualTypeArguments).forEach(type -> {
            ret.add(((Class)type).getName());
        });
        return ret;
    }
    public static void main(String[] args) {
        //  匿名的子类实现
        GenericApply genericApply = new GenericApply<Integer,Boolean>() {};
        System.out.println(genericApply.getGenericClassName());
    }
}
1234567891011121314151617181920212223242526

运行结果:

网络异常,图片无法展示
|

可以看到,GenericApply 这类上有两个泛型参数,使用上面的方法后,咱们可以得到全面的泛型全类名。

注意: 类上加泛型,最好使用在抽象类上或者接口类上。

场景二

泛型在抽象类和接口类上,我们怎么运用获取呢,展示下代码。

这里划分了三个类,接口类、抽象类、实现类。

接口类

public interface IGeneric<I> {
    void process(I i);
}
123

抽象类

public abstract class AbstractGeneric<T> {
    // 当前泛型真实类型的Class
    private final Class<T> modelClass;
    public AbstractGeneric(){
        ParameterizedType parameterizedType = (ParameterizedType)this.getClass().getGenericSuperclass();
        modelClass = (Class<T>)parameterizedType.getActualTypeArguments()[0];
    }
    public Class<T> getGeneric(){
        return modelClass;
    }
}
1234567891011121314

实现类

public class GenericImpl extends AbstractGeneric<String> implements IGeneric<Boolean>{
    @Override
    public void process(Boolean param) {
    }
    public static void main(String[] args) {
        GenericImpl generic = new GenericImpl();
        System.out.println("抽象类上的泛型全类名 "+generic.getGeneric().getName()+"\n");
        Type[] genericInterfaces = generic.getClass().getGenericInterfaces();
        for (Type genericInterface : genericInterfaces) {
            Type[] actualTypeArguments = ((ParameterizedType) genericInterface).getActualTypeArguments();
            Stream.of(actualTypeArguments).forEach(type -> {
                System.out.println("接口类上的泛型全类名 "+((Class) type).getName());
            });
        }
    }
}
123456789101112131415161718192021

运行结果:

网络异常,图片无法展示
|

看到运行结果可以打印出抽象类上后者接口上的泛型,这种应该是咱们经常使用的方式。

场景三

还有种更高级的用法,这种用法是和注解一起用的。用于标记泛型。

咱们在上面可以看到泛型参数返回来的是个数组,也就是咱们必须知道这个类的泛型位置,才能找到数组上对应位置的泛型类。
有没有一种办法,我不通过数组下标呢。其实是有的,咱们可以通过注解的方式,标定我们的泛型类,不是很复杂,咱们可以一起来看下。直接简单看下代码。

注解类

//相当于标注,找到注解为 value 值的 泛型类
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})
public @interface MyAxis {
    String value() default "";
}
123456

接口泛型类

//相当于标注,找到注解为 value 值的 泛型类
public interface IMultiParamInterface<@MyAxis(FIRST) T extends String,@MyAxis(SECONDE) U extends Integer> {
    String FIRST="FIRST";
    String SECONDE="SECONDE";
    void process();
}
1234567

实现类

public class MultiParamsImpl implements IMultiParamInterface<String,Integer>{
    @Override
    public void process() {
        System.out.println("MultiParamsImpl is invoke process");
    }
    //测试
    public static void main(String[] args) {
        Class<MultiParamsImpl> multiParamsClass = MultiParamsImpl.class;
        //得到这个接口上的 所有泛型
        Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(multiParamsClass, IMultiParamInterface.class);
        for (Map.Entry<TypeVariable<?>, Type> typeVariableTypeEntry : typeArguments.entrySet()) {
            TypeVariable<?> key = typeVariableTypeEntry.getKey();
            MyAxis annotation = AnnotationUtils.getAnnotation(key, MyAxis.class);
            Type value = typeVariableTypeEntry.getValue();
            System.out.println("名称为:"+ annotation.value() +" 泛型类全限定名: "+value.getTypeName());
        }
    }
}
1234567891011121314151617181920

上面就是接口上有多个泛型,分别被标注为不同的名字,便于正确获取到想要的泛型类型。

运行结果:

网络异常,图片无法展示
|

总结

上面列举了泛型与抽象列,接口,注解在一起的多种运用和获取方式。泛型还是很重要的,希望我上面列举的对朋友们有点儿帮助。另外帮忙多点点赞呗,有什么疑问,大家可以评论区指出。

本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。

相关文章
|
监控 API UED
Elasticsearch 异步搜索 Async search 实战
1、Elasticsearch 异步搜索定义 异步搜索 API 可异步执行搜索请求、监控其进度并检索可用的部分结果。 如下的官方介绍动画,能更加生动的介绍清楚异步检索。 传统检索 VS 异步检索,在数据量比较大时: 传统检索可能导致超时,以至于无数据返回;或者需要等待很久,用户体验差。 异步检索,可以快速响应数据,用户无需等待。
Elasticsearch 异步搜索 Async search 实战
|
Java 数据库连接 开发者
Mybatis Plus公共字段自动填充(MyMetaObjectHandler)
Mybatis Plus公共字段自动填充(MyMetaObjectHandler)
997 0
|
12月前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习的原理与应用:开启智能时代的大门
深度学习的原理与应用:开启智能时代的大门
733 16
|
缓存 NoSQL Redis
Redis 缓存使用的实践
《Redis缓存最佳实践指南》涵盖缓存更新策略、缓存击穿防护、大key处理和性能优化。包括Cache Aside Pattern、Write Through、分布式锁、大key拆分和批量操作等技术,帮助你在项目中高效使用Redis缓存。
1285 22
报错org.springframework.jdbc.UncategorizedSQLException: Error attempting to get column ‘xxx‘ from resu
报错org.springframework.jdbc.UncategorizedSQLException: Error attempting to get column ‘xxx‘ from resu
报错org.springframework.jdbc.UncategorizedSQLException: Error attempting to get column ‘xxx‘ from resu
|
存储 安全 算法
CTF磁盘取证分析方法案例
CTF磁盘取证分析方法案例
543 0
|
应用服务中间件 nginx Docker
Docker部署Nginx以及挂载数据卷(代码详细展示)_nginx 挂载大文件卷(3)
Docker部署Nginx以及挂载数据卷(代码详细展示)_nginx 挂载大文件卷(3)
|
NoSQL Java Redis
利用Docker部署一个简单的springboot项目
利用Docker部署一个简单的springboot项目
478 2
|
存储 Oracle 关系型数据库
oracle 数据库 迁移 mysql数据库
将 Oracle 数据库迁移到 MySQL 是一项复杂的任务,因为这两种数据库管理系统具有不同的架构、语法和功能。
278 0
|
IDE Linux 网络安全
使用vscode远程连接服务器或虚拟机进行编码
使用vscode远程连接服务器或虚拟机进行编码
1275 0