为什么不建议使用 Map 传递参数

3,338 阅读3分钟

这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战

前言

虽然我嘴上不建议使用 Map 作为接口参数,但是实际上我却在使用中,真的是…… 真香。下面我就说说在使用 Map 作为接口参数的时候遇到的坑以及我们为什么使用 Map 作为接口参数。

首先,不得不承认,Map 可以全包,所有的参数,各种参数都可以直接往 Map 中放,这样就很方便,接口不用改,新增参数也不用担心,随时加上去。灵活性非常高。扩展性也非常好。但是维护起来非常不方便,因为 Map 中有什么东西,只有看代码才知道,可读性也不好,还有一些类型转换的坑,下面重点说一下。

Long 类型转化为 Integer

map 作为接口参数在微服务之间传递时,会经过序列化和反序列化,问题就在于反序列化之后会把 Long 类型的值转化为 Integer 类型(这个数值在 int 范围内的话)。举个例子大家看一下。

// 服务 1
Map<String, Object> map = new HashMap<>();
Long age = 18;
map.put("age", age);
feignClient.transfer(map);
​
// 服务 2
transfer(Map<String, Object> map){
    Long age = (Long) map.get("age");
}

上面是一段伪代码哈,但是足以能够说明问题了,首先一个问题就是 Long 类型的值经过服务之间传递之后类型信息就变了,从 Long 变为 Integer,其次,这里还有一个隐患,就是可能会出现空指针异常,假如 map.get("age") 是空,这里就会报错。

Date 类型转化为 Long

直接上代码,大家看的清楚

// 服务 1
Map<String, Object> map = new HashMap<>();
Date date = new Date();
map.put("date", date);
feignClient.transfer(map);
​
// 服务 2
transfer(Map<String, Object> map){
    Long timeLong = map.get("date");
    Date date = new Date(timeLong);
}

这里的原理是一样的,在反序列化之后 Date 类型的数据会被转化为 Long 的数值,我们想要得到 Date 类型,还是需要手动做一次处理,而且这里也会可能出现空指针异常的情况,如果服务 1 map.put("date", null),在服务 2 中就会报错,我们还需要手动的判空。

代码不可维护

上面的例子我们也可以感受出来使用 map 作为接口参数真是方便,但是更多的还有 hard code 的问题,map 中的 key 谁能知道,你知道一个知道多个嘛?今天这个 key 使用,明天可能就不用了,你有能清楚嘛。即使你知道了 key ,是不是还需要空判断。这一通下来,就是写的时候爽维护的时候骂娘。

假如我们使用 Object 不管是 DTO 还是 TO 作为参数,有什么参数我们都可以封装在类中,是不是就会比较好呢?是会的,比方说我们的校验就会方便很多,使用 spring 和 hibernate 一起提供的 validator 还有一些注解,可以很优雅的解决数据校验的问题。但是有一个问题就是,由于参数的类型或是个数可能会不断的变化,这个对象也需要不断的变化。

总结

在不是必须的情况,都不建议使用 map 来传递参数,我们完全可以创建一个类来封装参数,这样做的好处很明显,代码可读,看起来非常清晰,类型定义清晰,如果想要添加新的参数只需要在类中添加即可。而且类中还可以定义 getter setter 方法,使用起来更舒适。

反观使用 map 你要知道所有的 key 以及对应的类型。如果这个 map 不幸被多个层级使用,这些 key 要在每层都知道…… map 虽然在写的时候很爽,但是大概率在运行的时候会有问题,不信你试试看?

最后说一下我们为什么还要使用 map 呢,因为 key 太多了…… 而且 key 的数量一直不能确定,所以先就使用 map 了。