巧用泛型接口抽取简化代码

1.Java泛型的好处

Java泛型是一种强大的编程特性,它可以在编译时提供类型安全性,并且可以使代码更加灵活和可重用。以下是Java泛型的几个好处:

  1. 类型安全:泛型可以在编译时捕获类型错误,避免在运行时出现类型转换异常。通过使用泛型,可以确保代码在编译时就能够检查到类型不匹配的错误,提高代码的可靠性。
  2. 代码重用:泛型可以使代码更加通用,可以在不同的数据类型上进行操作,从而提高代码的重用性。通过定义泛型类或方法,可以避免编写重复的代码,减少代码冗余。
  3. 集合类型安全:使用泛型可以确保集合中只能存储指定类型的对象,避免了在使用集合时进行类型转换的麻烦。通过使用泛型集合类,可以提高代码的可读性和可维护性。
  4. 编译时类型检查:泛型可以在编译时对类型进行检查,减少了运行时的错误。通过使用泛型,可以避免因为类型不匹配而导致的运行时异常,提高了代码的健壮性。

当不使用泛型时:

img

使用泛型时:

img

2.泛型接口的简单使用

Java泛型接口是一种可以在接口中使用泛型类型的特性。通过使用泛型接口,我们可以在接口中定义一种通用的类型,使得接口的实现类可以根据具体的需求指定不同的类型。

使用Java泛型接口的步骤如下:

  1. 在定义接口时,在接口名后面使用尖括号<>来声明泛型类型。例如:interface MyInterface<T> { ... }
  2. 在接口中可以使用泛型类型T来定义方法的参数类型、返回值类型或者成员变量的类型。
  3. 在实现该接口的类中,可以指定具体的类型来替代泛型类型T。

下面是一个示例代码,展示了如何使用Java泛型接口:

// 定义一个泛型接口
interface MyInterface<T> {
    void doSomething(T item);
}

// 实现泛型接口
class MyClass implements MyInterface<String> {
    @Override
    public void doSomething(String item) {
        System.out.println("Doing something with: " + item);
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建实现类对象
        MyClass myObj = new MyClass();
        // 调用接口方法
        myObj.doSomething("Hello");
    }
}

在上面的示例中,我们定义了一个泛型接口MyInterface,其中的方法doSomething接受一个泛型类型的参数。然后我们创建了一个实现类MyClass,并指定了泛型类型为String。最后在main方法中,我们创建了MyClass的对象,并调用了接口方法。

当然上面的接口也可以换成抽象类。

3.结合具体业务场景的示例

业务场景:

需要在不同的场合中调用同一个外部接口,这个外部接口组装参数比较复杂,然后组装参数的数据来源,不同的业务场景来源不一样,调完外部接口后,需要解析接口返回数据,并需要将返回结果记录更新回填到调用方的数据表中,如调用成功或者失败,失败的原因。

分析:

如果每个业务场景都各自组装参数,然后调用接口,解析数据,保存返回的结果,这种做法固然可以,但是代码重复度比较高,而且不利于维护,如果接口一个地方改动,比如请求参数或者返回结果的结构,那么就需要改动很多的地方。这种做法显然不是很好的。

根据分析,这里可以将组装参数,查询组装参数需要的数据的逻辑以及将调用接口后返回结果进行解析的逻辑进行抽取公共方法,但是问题就是各个场景的业务实体和表都不一样,这样的话抽取方法不是很好做,如果都采用Object类型进行传参,那势必后面会进行很多判断和类型转换,而且会增加参数,增加代码的复杂度,可读性会减弱,而且容易出错。

如果采用泛型的形式,刚好可以满足要求。这里直接上代码,注意:这里没有包含所有代码,是伪代码,主要是介绍过程和思路。

抽象类

/**
 * 同步钱包上账接口父类
 * T为单据申请单据头信息实体(包含基本的人员、职位、部门等信息,同步上账接口需要),X为申请明细数据实体,M为更新明细数据的DAO
 *
 * @author xiaohuihui
 * @creteTime 2024-03-24 17:09
 * @description
 */
public abstract class SyncWalletService<T, X, M extends BaseMapper<X>> {
    
    public void syncCashWallet(T head, M dao) {
        List<X> cashCostList = getCashCostList(head);//getCashCostList为抽象方法,给子类实现
        if (CollectionUtils.isEmpty(cashCostList)) {
            LOGGER.info("单据同步提现钱包列表为空,不需要同步,{}", JSONObject.toJSONString(head));
            return;
        }
        for (X cashCost : cashCostList) {
            //组装请求参数
            AddUserWalletReq cashReq = buildCashReq(head, cashCost);//buildCashReq,给子类实现
            LOGGER.info("同步提现钱包请求参数:{}", JSONObject.toJSONString(JSONObject.toJSONString(cashReq)));
            JSONObject cashSyncResult = this.addUserWalletAmount(cashReq);//调用接口
            LOGGER.info("同步提现钱包返回结果:{}", JSONObject.toJSONString(JSONObject.toJSONString(cashSyncResult)));
            if ("S".equals(cashSyncResult.getString("status"))) {
                //取出返回的结果
                SyncEmsServiceUtil.setValueByPropertyName(cashCost, "syncWalletResult", "S");
                SyncEmsServiceUtil.setValueByPropertyName(cashCost, "syncWalletFailReason", StringUtils.EMPTY);
            } else {
                String errorMsg = cashSyncResult.getString("msg");
                SyncEmsServiceUtil.setValueByPropertyName(cashCost, "syncWalletResult", "E");
                SyncEmsServiceUtil.setValueByPropertyName(cashCost, "syncWalletFailReason", errorMsg);
            }
            //保存返回的结果
            dao.updateById(cashCost);
        }
    }
    //查询数据
    protected abstract List<X> getCashCostList(T head);
    //组装请求参数
    protected abstract AddUserWalletReq buildCashReq(T head, X cashCost);
    
}

上面这里用到了一个工具类SyncEmsServiceUtil,用了一个比较通用的办法去给一个对象中的属性赋值

import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

public class SyncEmsServiceUtil {
    
    /**
     * 根据属性名称和类型获取对象对应的属性值
     * @param obj
     * @param propName
     * @param resultType
     * @return
     */
    public static Object getValueByPropertyName(Object obj, String propName, Class<?> resultType) {
        MetaObject meta = SystemMetaObject.forObject(obj);
        if (meta.hasGetter(propName)) {
            System.out.println(meta.getGetterType(propName).getName());
            if (meta.getValue(propName) instanceof Integer) {
                if (resultType.isAssignableFrom(Long.class)) {
                    return Long.valueOf((Integer) meta.getValue(propName));
                }
            } else if (meta.getValue(propName) instanceof Long) {
                if (resultType.isAssignableFrom(Integer.class)) {
                    return Math.toIntExact((Long) meta.getValue(propName));
                }
            }
        } else {
            throw new IllegalArgumentException(obj.getClass() + "中没有属性" + propName + "的get方法");
        }
        return meta.getValue(propName);
    }

    /**
     * 根据属性名称设置对象的属性值
     * @param obj
     * @param propName
     * @param value
     */
    public static void setValueByPropertyName(Object obj, String propName, Object value) {
        MetaObject meta = SystemMetaObject.forObject(obj);
        if (meta.hasSetter(propName)) {
            meta.setValue(propName, value);
        } else {
            throw new IllegalArgumentException(obj.getClass() + "中没有属性" + propName + "的set方法");
        }
    }
    
}

实现类

public class CloseCaseSyncWalletServiceImpl extends SyncWalletService<StoreRebateCloseApplyHead, StoreRebateReportCost, StoreRebateReportCostDao> {
@Autowired
    private StoreRebateReportCostDao storeRebateReportCostDao;

    @Override
    public List<StoreRebateReportCost> getCashCostList(StoreRebateCloseApplyHead head) {
        return this.storeRebateReportCostDao.selectReportCostForSyncWallet(head.getCloseHeadApplyId(), StoreRebateConstants.WalletType.CASH_OUT_WALLET, null);
    }


    @Override
    public AddUserWalletReq buildCashReq(StoreRebateCloseApplyHead head, StoreRebateReportCost cashCost) {
        return AddUserWalletReq.builder().amount(cashCost.getCloseAmount())
                .account(cashCost.getReceiveObjectAccount())
                .sourceId(String.valueOf(cashCost.getReportCostId()))
                .orgId(Math.toIntExact(head.getOrgId()))
                .build();
    }

    
}

上面只是其中的一个实现类,其他实现类实现方式一样,这里省略了。

客户端的调用

 

public class StoreRebateAdjustAddApplyHeadServiceImpl{
     @Autowired
     private StoreRebateReportCostDao storeRebateReportCostDao;
    
    private void syncWallet(StoreRebateCloseApplyHead head) {
            syncWalletService.syncCashWallet(head, storeRebateReportCostDao);
     }
    
}

这里的dao(StoreRebateReportCostDao) M 和 实体 X(StoreRebateReportCost) 之间有这样的关系:M extends BaseMapper<X>

image-20240324222034840

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface StoreRebateReportCostDao extends BaseMapper<StoreRebateReportCost> {
}

这里用的是MyBatis的框架,StoreRebateReportCostDao为Mapper层或者叫Dao层。

相关推荐

  1. C#接口

    2024-03-24 23:34:02       47 阅读
  2. ts之接口概念

    2024-03-24 23:34:02       37 阅读
  3. 编程-常模板

    2024-03-24 23:34:02       44 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-24 23:34:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-24 23:34:02       101 阅读
  3. 在Django里面运行非项目文件

    2024-03-24 23:34:02       82 阅读
  4. Python语言-面向对象

    2024-03-24 23:34:02       91 阅读

热门阅读

  1. P2123皇后游戏

    2024-03-24 23:34:02       42 阅读
  2. Linux简单基础配置

    2024-03-24 23:34:02       41 阅读
  3. (c/c++)——线程的基础使用

    2024-03-24 23:34:02       37 阅读
  4. rust - 将bitmap位图文件另存为png格式

    2024-03-24 23:34:02       41 阅读
  5. PostgreSQL与MySQL对比

    2024-03-24 23:34:02       46 阅读
  6. jvm底层

    jvm底层

    2024-03-24 23:34:02      36 阅读
  7. python

    2024-03-24 23:34:02       43 阅读
  8. 【机器学习-09】特征工程

    2024-03-24 23:34:02       34 阅读
  9. js和jsp的区别

    2024-03-24 23:34:02       37 阅读