硬编码 java_使用java8的方法引用替换硬编码

背景

想必大家在项目中都有遇到把一个列表的多个字段累加求和的情况,也就是一个列表的总计。有的童鞋问,这个不是给前端做的吗?后端不是只需要把列表返回就行了嘛。。。没错,我也是这样想的,但是在一场和前端的撕逼大战中败下阵来之后,这个东西就落在我身上了。当时由于工期原因,时间比较紧,也就不考虑效率和易用性了,只是满足当时的需求,就随便写了个方法统计求和。目前稍微闲下来了,就把原来的代码优化下。我们先来看一下原来的代码...

原代码

工具类

importorg.apache.commons.lang3.StringUtils;importorg.springframework.util.CollectionUtils;importjava.lang.reflect.Method;importjava.math.BigDecimal;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.List;/***  * @ClassName CalculationUtil

*  * @Description TODO(计算工具类)

*  * @Author 我恰芙蓉王

*  * @Date 2020年04月21日 11:37

*  * @Version 1.0.0

*

**/

public classCalculationUtil {//拼接get set方法的常量

public static final String GET = "get";public static final String SET = "set";/*** 功能描述: 公用统计小计方法

*

*@paramlist 原数据列表集合

*@paramfields 运算的属性数组

* @创建人: 我恰芙蓉王

* @创建时间: 2020年05月12日 17:50:09

*@return: org.apache.poi.ss.formula.functions.T 返回统计好的对象

**/

public static T totalCalculationForBigDecimal(List list, String... fields) throwsException {if(CollectionUtils.isEmpty(list)) {return null;

}

Class clazz= list.get(0).getClass();//返回值

Object object =clazz.newInstance();

list.stream().forEach(v->Arrays.asList(fields).parallelStream().forEach(t->{try{

String field=StringUtils.capitalize(t);//获取get方法

Method getMethod = clazz.getMethod(GET +field);//获取set方法

Method setMethod = clazz.getMethod(SET + field, BigDecimal.class);

Object objectValue=getMethod.invoke(object);

setMethod.invoke(object, (objectValue== null ?BigDecimal.ZERO : (BigDecimal) objectValue).add((BigDecimal) getMethod.invoke(v)));

}catch(Exception e) {

e.printStackTrace();

}

})

);return(T) object;

}/*** 功能描述: 公用统计小计方法

*

*@paramlist 原数据列表集合

*@paramfields 运算的属性数组

* @创建人: 我恰芙蓉王

* @创建时间: 2020年05月12日 17:50:09

*@return: org.apache.poi.ss.formula.functions.T 返回统计好的对象

**/

public static T totalCalculationForDouble(List list, String... fields) throwsException {if(CollectionUtils.isEmpty(list)) {return null;

}

Class clazz= list.get(0).getClass();//返回值

Object object =clazz.newInstance();

list.stream().forEach(v->Arrays.asList(fields).parallelStream().forEach(t->{try{

String field=StringUtils.capitalize(t);//获取get方法

Method getMethod = clazz.getMethod(GET +field);//获取set方法

Method setMethod = clazz.getMethod(SET + field, Double.class);

Object objectValue=getMethod.invoke(object);

setMethod.invoke(object, add((objectValue== null ? new Double(0) : (Double) objectValue), (Double) getMethod.invoke(v)));

}catch(Exception e) {

e.printStackTrace();

}

})

);return(T) object;

}/*** 功能描述: 公用统计小计方法

*

*@paramlist 原数据列表集合

*@paramfields 运算的属性数组

* @创建人: 我恰芙蓉王

* @创建时间: 2020年05月12日 17:50:09

*@return: org.apache.poi.ss.formula.functions.T 返回统计好的对象

**/

public static T totalCalculationForFloat(List list, String... fields) throwsException {if(CollectionUtils.isEmpty(list)) {return null;

}

Class clazz= list.get(0).getClass();//返回值

Object object =clazz.newInstance();

list.stream().forEach(v->Arrays.asList(fields).parallelStream().forEach(t->{try{

String field=StringUtils.capitalize(t);//获取get方法

Method getMethod = clazz.getMethod(GET +field);//获取set方法

Method setMethod = clazz.getMethod(SET + field, Float.class);

Object objectValue=getMethod.invoke(object);

setMethod.invoke(object, add((objectValue== null ? new Float(0) : (Float) objectValue), (Float) getMethod.invoke(v)));

}catch(Exception e) {

e.printStackTrace();

}

})

);return(T) object;

}/*** 提供精确的加法运算。

*

*@paramv1 被加数

*@paramv2 加数

*@return两个参数的和*/

public staticDouble add(Double v1, Double v2) {

BigDecimal b1= newBigDecimal(v1.toString());

BigDecimal b2= newBigDecimal(v2.toString());returnb1.add(b2).doubleValue();

}/*** 提供精确的加法运算。

*

*@paramv1 被加数

*@paramv2 加数

*@return两个参数的和*/

public staticFloat add(Float v1, Float v2) {

BigDecimal b1= newBigDecimal(v1.toString());

BigDecimal b2= newBigDecimal(v2.toString());returnb1.add(b2).floatValue();

}

}

实体类

@Data

@AllArgsConstructor

@NoArgsConstructorpublic classOrder {//订单号

privateString orderNo;//订单金额

privateDouble money;//折扣

privateDouble discount;

}

@Data

@AllArgsConstructor

@NoArgsConstructorpublic classPhone {//手机名

privateString name;//成本

privateBigDecimal cost;//售价

privateBigDecimal price;

}

测试

public static void main(String[] args) throwsException {

List orderList = new ArrayList() {

{

add(new Order("D20111111", 256.45, 11.11));

add(new Order("D20111112", 123.85, 1.11));

add(new Order("D20111113", 546.13, 2.14));

add(new Order("D20111114", 636.44, 0.88));

}

};

List phoneList = new ArrayList() {

{

add(new Phone("苹果", new BigDecimal("123.11"), new BigDecimal("222.22")));

add(new Phone("三星", new BigDecimal("123.11"), new BigDecimal("222.22")));

add(new Phone("华为", new BigDecimal("123.11"), new BigDecimal("222.22")));

add(new Phone("小米", new BigDecimal("123.11"), new BigDecimal("222.22")));

}

};

Order orderTotal= totalCalculationForDouble(orderList, "money", "discount");

System.out.println("总计数据为 :" +orderTotal);

Phone phoneTotal= totalCalculationForBigDecimal(phoneList, "cost", "price");

System.out.println("总计数据为 :" +phoneTotal);

}

0c7e776ed7f66a7f14be34557fa3f246.png

通过以上代码可以看出,效果是实现了,但是缺点也是很明显的:

1.太过冗余,相同代码太多,多个方法只有少数代码不相同(工具类中黄色标注的地方);

2.效率低,列表中每个元素的每个属性都要用到反射赋值;

3.灵活性不够,要求实体类中需要参加运算的属性都为同一类型,即必须都为Double,或必须都为BigDecimal;

4.硬编码,直接在方法调用时把实体类中的字段写死,既不符合JAVA编码规范也容易出错,而且当该实体类中的属性名变更的时候,IDE无法提示我们相应的传参的变更,极容易踩坑。

因为项目中用的JDK版本是1.8,当时在写的时候就想通过方法引用规避掉这种硬编码的方式,因为在Mybatis-Plus中也有用到方法引用赋值条件参数的情况,但还是因为时间紧急,就没去研究了。

今天就顺着这个方向去找了一下实现的方法,把代码优化了部分,如下:

优化后

首先,我是想通过传参为方法引用的方式来获取Getter方法对应的属性名,通过了解,JDK8中已经给我们提供了实现方式,首先声明一个自定义函数式接口(需要实现Serializable)

@FunctionalInterfacepublic interface SerializableFunction extends Function, Serializable {

}

然后定义一个反射工具类去解析这个自定义函数式接口,在此工具类中有对方法引用解析的具体实现,在此类中规避掉缺点4

importorg.apache.commons.lang3.StringUtils;importorg.springframework.util.ClassUtils;importorg.springframework.util.ReflectionUtils;importjava.lang.invoke.SerializedLambda;importjava.lang.reflect.Field;importjava.lang.reflect.Method;/*** @ClassName ReflectionUtil

* @Description TODO(反射工具类)

* @Author 我恰芙蓉王

* @Date 2020年09月08日 15:10

* @Version 2.0.0

**/

public classReflectionUtil {public static final String GET = "get";public static final String SET = "set";/*** 功能描述: 通过get方法的方法引用返回对应的Field

*

*@paramfunction

* @创建人: 我恰芙蓉王

* @创建时间: 2020年09月08日 16:20:56

*@return: java.lang.reflect.Field

**/

public static Field getField(SerializableFunctionfunction) {try{/*** 1.获取SerializedLambda*/Method method= function.getClass().getDeclaredMethod("writeReplace");

method.setAccessible(Boolean.TRUE);/*** 2.利用jdk的SerializedLambda,解析方法引用,implMethodName 即为Field对应的Getter方法名*/SerializedLambda serializedLambda=(SerializedLambda) method.invoke(function);//获取get方法的方法名

String getter =serializedLambda.getImplMethodName();//获取属性名

String fieldName = StringUtils.uncapitalize(getter.replace(GET, ""));/*** 3.获取的Class是字符串,并且包名是“/”分割,需要替换成“.”,才能获取到对应的Class对象*/String declaredClass= serializedLambda.getImplClass().replace("/", ".");

Class clazz= Class.forName(declaredClass, false, ClassUtils.getDefaultClassLoader());/*** 4.通过Spring中的反射工具类获取Class中定义的Field*/

returnReflectionUtils.findField(clazz, fieldName);

}catch(ReflectiveOperationException e) {throw newRuntimeException(e);

}

}

}

接着改写原来计算工具类中的代码,在此类中将原缺点的1,2,3点都规避了,将原来冗余的多个方法精简成一个 totalCalculation ,通过 methodMap 对象将get,set方法缓存(但此缓存还有优化的空间,可以将方法中的缓存对象提到tomcat内存或redis中),通过动态获取字段类型来实现不同类型的累加运算

importorg.apache.commons.lang3.StringUtils;importorg.springframework.util.CollectionUtils;importjava.lang.reflect.Constructor;importjava.lang.reflect.Field;importjava.lang.reflect.Method;importjava.math.BigDecimal;import java.util.*;importjava.util.concurrent.ConcurrentHashMap;import staticio.renren.modules.test1.ReflectionUtil.GET;import staticio.renren.modules.test1.ReflectionUtil.SET;/***  * @ClassName CalculationUtil

*  * @Description TODO(计算工具类)

*  * @Author 我恰芙蓉王

*  * @Date 2020年04月21日 11:37

*  * @Version 1.0.0

*

**/

public classCalculationUtil {/*** 功能描述: 公用统计小计方法

*

*@paramlist 原数据列表集合

*@paramfunctions 参与运算的方法引用

* @创建人: 我恰芙蓉王

* @创建时间: 2020年05月12日 17:50:09

*@return: org.apache.poi.ss.formula.functions.T 返回统计好的对象

**/

public static T totalCalculation(List list, SerializableFunction... functions) throwsException {if(CollectionUtils.isEmpty(list)) {return null;

}//获取集合中类型的class对象

Class clazz = list.get(0).getClass();//Getter Setter缓存

Map> methodMap = new ConcurrentHashMap<>();//遍历字段,将Getter Setter放入缓存中

for(SerializableFunction function : functions) {

Field field=ReflectionUtil.getField(function);//获取get方法

Method getMethod = clazz.getMethod(GET +StringUtils.capitalize(field.getName()));//获取set方法

Method setMethod = clazz.getMethod(SET +StringUtils.capitalize(field.getName()), field.getType());//将get set方法封装成一个map放入缓存中

methodMap.put(function, new HashMap() {

{

put(GET, getMethod);

put(SET, setMethod);

}

});

}//计算

T result = list.parallelStream().reduce((x, y) ->{try{

Object newObject=x.getClass().newInstance();

Arrays.asList(functions).parallelStream().forEach(f->{try{

Map fieldMap =methodMap.get(f);//获取缓存的get方法

Method getMethod =fieldMap.get(GET);//获取缓存的set方法

Method setMethod =fieldMap.get(SET);//调用x参数t属性的get方法

Object xValue =getMethod.invoke(x);//调用y参数t属性的get方法

Object yValue =getMethod.invoke(y);//反射赋值到newObject对象

setMethod.invoke(newObject, add(xValue, yValue, getMethod.getReturnType()));

}catch(Exception e) {

e.printStackTrace();

}

});return(T) newObject;

}catch(Exception e) {

e.printStackTrace();

}return null;

}).get();returnresult;

}/*** 功能描述: 提供精确的加法运算

*

*@paramv1 加数

*@paramv2 被加数

*@paramclazz 参数的class类型

* @创建人: 我恰芙蓉王

* @创建时间: 2020年09月08日 10:55:56

*@return: java.lang.Object 相加之和

**/

public static Object add(Object v1, Object v2, Class clazz) throwsException {

BigDecimal b1= newBigDecimal(v1.toString());

BigDecimal b2= newBigDecimal(v2.toString());

Constructor constructor= clazz.getConstructor(String.class);returnconstructor.newInstance(b1.add(b2).toString());

}

}

测试实体类

@Data

@AllArgsConstructor

@NoArgsConstructorpublic classPeople {//名字

privateString name;//年龄

privateInteger age;//存款

privateBigDecimal money;//身高

privateDouble height;

}

调用

public static void main(String[] args) throwsException {

List list = new ArrayList() {

{

add(new People("张三", 18, BigDecimal.valueOf(10000), 168.45));

add(new People("李四", 20, BigDecimal.valueOf(20000), 155.68));

add(new People("王五", 25, BigDecimal.valueOf(30000), 161.54));

add(new People("赵六", 21, BigDecimal.valueOf(30000), 166.66));

}

};

People total=CalculationUtil.totalCalculation(list, People::getAge, People::getMoney, People::getHeight);

System.out.println("总计数据为 :" +total);

}

7b3a269cba2e5c5a0d7b919382085199.png

总结

java8的lambda表达式确实极大的简化了我们的代码,提高了编码的效率,流计算更是使数据的运算变得高效快捷,也增加了代码的可(zhuang)读(bi)性。如今java14都出来了,希望在空余时间也能多去了解一下新版本的新特性,而不能老是抱着(你发任你发,我用java8)的心态去学习,毕竟技术的更新迭代是极快的。

参考博文:https://blog.csdn.net/u013202238/article/details/105779686

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值