【优雅代码】02-java传个方法你会吗,不是Method对象

欢迎关注b站账号/公众号【六边形战士夏宁】,一个要把各项指标拉满的男人。该文章已在github目录收录。

屏幕前的大帅比大漂亮如果有帮助到你的话请顺手点个赞、加个收藏这对我真的很重要。别下次一定了,都不关注上哪下次一定。

1.背景介绍

在日常代码中有时候近乎避免不了的使用魔法值,但是如果使用传入方法这种方式可以极大的降低魔法值出现的频率并且不用创建静态值。该方法主要参考了mybatisPlus,并在此基础上进行了扩展。

核心部分(1)IFN,该类java有自带的Function,使用方法一致,可用于各种get方法的传入,如果是set方法则使用Consumer。本质上调用时第一个参数传入对象,和反射的用法非常相似

(2)使用writeReplace,该部分为重写Serializable中的方法

2.相关代码

  • IFn
package com.example.demo.bean;

import java.io.Serializable;

/**
* F 传入类型,T返回类型
* @author seal
*/
@FunctionalInterface
public interface IFn<F, T> extends Serializable {
T apply(F source);
}
  • IFnVoid
package com.example.demo.bean;

import java.io.Serializable;

/**
* F 传入类型,T返回类型
* @author seal
*/
@FunctionalInterface
public interface IFnVoid<F,T> extends Serializable {
void apply(F source,T arg);
}
  • Reflections
package com.example.demo.bean;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.mapping.Field; import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method; /**
* 传入方法后的实现逻辑
*
* @author seal 876651109@qq.com
* @date 7/12/2020 7:20 PM
*/
@Slf4j
public class Reflections {
private Reflections() {
} public static <F, T> String fnToFieldName(IFn<F, T> fn) {
SerializedLambda serializedLambda = getSerializedLambda(fn);
String getter = serializedLambda.getImplMethodName();
String fieldName = "";
if (getter.startsWith("get")) {
fieldName = Introspector.decapitalize(getter.replace("get", ""));
}
return fieldName;
} public static <F, T> String fnToFnName(IFn<F, T> fn) {
return getSerializedLambda(fn).getImplMethodName();
} public static <F, T> Method fnToMethod(IFn<F, T> fn) {
SerializedLambda serializedLambda = getSerializedLambda(fn);
Method method = null;
try {
method = getClazz(serializedLambda).getDeclaredMethod(serializedLambda.getImplMethodName());
} catch (NoSuchMethodException e) {
log.error("", e);
}
return method;
} public static <F, T> Field fnToField(IFn<F, T> fn) {
SerializedLambda serializedLambda = getSerializedLambda(fn);
String fieldName = "";
String getter = serializedLambda.getImplMethodName();
if (getter.startsWith("get")) {
fieldName = Introspector.decapitalize(getter.replace("get", ""));
}
Class clazz = getClazz(serializedLambda);
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
ReflectionUtils.makeAccessible(field);
} catch (NoSuchFieldException e) {
log.error("", e);
}
return field;
} public static <F, T> String fnToFieldNameVoid(IFnVoid<F, T> fn) {
SerializedLambda serializedLambda = getSerializedLambda(fn);
String getter = serializedLambda.getImplMethodName();
String fieldName = "";
if (getter.startsWith("set")) {
fieldName = Introspector.decapitalize(getter.replace("set", ""));
}
return fieldName;
} private static SerializedLambda getSerializedLambda(Object fn) {
SerializedLambda serializedLambda = null;
try {
Method method = fn.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(Boolean.TRUE);
serializedLambda = (SerializedLambda) method.invoke(fn);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error("", e);
}
return serializedLambda;
} private static Class getClazz(SerializedLambda serializedLambda) {
Class clazz = null;
try {
clazz = Class.forName(serializedLambda.getImplClass().replace("/", "."));
} catch (ClassNotFoundException e) {
log.error("", e);
}
return clazz;
} public static void main(String[] args) {
String fieldName = Reflections.fnToFieldName(ValidatedRequestVO::getStr);
System.out.println("字段名:" + fieldName);
String fnName = Reflections.fnToFnName(ValidatedRequestVO::getStr);
System.out.println("方法名:" + fnName);
String fieldName2 = Reflections.fnToFieldNameVoid(ValidatedRequestVO::setStr);
System.out.println("字段名:" + fieldName2);
Method method = Reflections.fnToMethod(ValidatedRequestVO::getStartDate);
System.out.println("注解:" + Reflections.fnToField(ValidatedRequestVO::getStartDate).getAnnotation(org.springframework.data.mongodb.core.mapping.Field.class).value());
}
}

3.举例应用

  1. 如视频中所展现的可以取方法/字段的注解
  2. 利用反射实现伪代理
  3. 最普遍的应用即mybatisPlus的应用,可以动态传入需要的字段和不需要的字段而不用改动sql,以此优化性能

平铺转树与树转平铺的个人工具类

public class TreeUtils {
public static void main(String[] args) {
List<TestTreeObj> list = new ArrayList<TestTreeObj>() {{
add(TestTreeObj.builder().id(1).build());
add(TestTreeObj.builder().id(11).pid(1).build());
add(TestTreeObj.builder().id(12).pid(1).build());
add(TestTreeObj.builder().id(111).pid(11).build());
add(TestTreeObj.builder().id(112).pid(11).build());
add(TestTreeObj.builder().id(121).pid(12).build());
add(TestTreeObj.builder().id(122).pid(12).build());
add(TestTreeObj.builder().id(2).build());
add(TestTreeObj.builder().id(21).pid(2).build());
add(TestTreeObj.builder().id(22).pid(2).build());
add(TestTreeObj.builder().id(211).pid(21).build());
add(TestTreeObj.builder().id(212).pid(21).build());
add(TestTreeObj.builder().id(221).pid(22).build());
add(TestTreeObj.builder().id(222).pid(22).build());
}};
List<TestTreeObj> treeResult = listToTree(list, TestTreeObj::setTestTreeObj, TestTreeObj::getId, TestTreeObj::getPid, (l) -> l.getPid() == 0); List<TestTreeObj> testTreeObjs = new ArrayList<TestTreeObj>() {{
add(TestTreeObj.builder().id(1).testTreeObj(new ArrayList<TestTreeObj>() {{
add(TestTreeObj.builder().id(11).testTreeObj(new ArrayList<TestTreeObj>() {{
add(TestTreeObj.builder().id(111).build());
add(TestTreeObj.builder().id(112).build());
}}).build());
}}).build());
}};
List<TestTreeObj> result = new ArrayList<>();
treeToListDeep(testTreeObjs, result, TestTreeObj::getTestTreeObj, (l) -> l.getTestTreeObj() == null);
List<TestTreeObj> result2 = new ArrayList<>();
treeToListDeep(testTreeObjs, result2, TestTreeObj::getTestTreeObj, (l) -> l.getPid() == 0);
System.out.println(result2);
} /**
* 树转平铺
* treeToListDeep(testTreeObjs, result, TestTreeObj::getTestTreeObj, (l) -> l.getTestTreeObj() == null);
*
* @param source 源数据
* @param target 目标容器
* @param childListFn 递归调用方法
* @param addTargetCondition 添加到容器的判断方法
* @author 876651109@qq.com
* @date 2021/3/1 8:19 下午
*/
public static <F> void treeToListDeep(List<F> source, List<F> target, Function<F, List<F>> childListFn, Predicate<F> addTargetCondition) {
if (CollectionUtils.isEmpty(source)) {
return;
}
for (F f : source) {
if (addTargetCondition.test(f)) {
target.add(f);
}
treeToListDeep(childListFn.apply(f), target, childListFn, addTargetCondition);
}
} /**
* List<TestTreeObj> treeResult = listToTree(list, TestTreeObj::setTestTreeObj, TestTreeObj::getId, TestTreeObj::getPid, (l) -> l.getPid() == 0);
*
* @param source 源数据
* @param childListFn 设置递归的方法
* @param idFn 获取id的方法
* @param pidFn 获取父id的方法
* @param getRootCondition 获取根节点的提哦啊见
* @return {@link List<F>}
* @author 876651109@qq.com
* @date 2021/3/1 8:18 下午
*/
public static <F, T> List<F> listToTree(List<F> source, BiConsumer<F, List<F>> childListFn, Function<F, T> idFn, Function<F, T> pidFn, Predicate<F> getRootCondition) {
List<F> tree = new ArrayList<>();
Map<T, List<F>> map = new HashMap<>();
for (F f : source) {
if (getRootCondition.test(f)) {
tree.add(f);
} else {
List<F> tempList = map.getOrDefault(pidFn.apply(f), new ArrayList<>());
tempList.add(f);
map.put(pidFn.apply(f), tempList);
}
}
tree.forEach(l -> assembleTree(l, map, childListFn, idFn));
return tree;
} private static <F, T> void assembleTree(F current, Map<T, List<F>> map, BiConsumer<F, List<F>> childListFn, Function<F, T> idFn) {
List<F> fs = map.get(idFn.apply(current));
if (CollectionUtils.isEmpty(fs)) {
return;
}
childListFn.accept(current, fs);
fs.forEach(l -> assembleTree(l, map, childListFn, idFn));
}
}

【优雅代码】02-java传个方法你会吗,不是Method对象的更多相关文章

  1. Android C代码回调java方法

    本文将讲述下列三种C代码回调java方法 1.c代码回调java空方法 2.c代码回调java int类型参数方法 3.c代码回调javaString类型参数方法 方法都差不多,先看c代码回调java ...

  2. Java-Runoob-高级教程-实例-方法:02. Java 实例 – 输出数组元素

    ylbtech-Java-Runoob-高级教程-实例-方法:02. Java 实例 – 输出数组元素 1.返回顶部 1. Java 实例 - 输出数组元素  Java 实例 以下实例演示了如何通过重 ...

  3. c#代码 天气接口 一分钟搞懂你的博客为什么没人看 看完python这段爬虫代码,java流泪了c#沉默了 图片二进制转换与存入数据库相关 C#7.0--引用返回值和引用局部变量 JS直接调用C#后台方法(ajax调用) Linq To Json SqlServer 递归查询

    天气预报的程序.程序并不难. 看到这个需求第一个想法就是只要找到合适天气预报接口一切都是小意思,说干就干,立马跟学生沟通价格. ​ ​不过谈报价的过程中,差点没让我一口老血喷键盘上,话说我们程序猿的人 ...

  4. 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 02 封装的代码实现

    088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 02 封装的代码实现 本文知识点:Java封装的代码实现 说明:因为时间紧张,本人写博客过程中只 ...

  5. 分享非常有用的Java程序(关键代码)(八)---Java InputStream读取网络响应Response数据的方法!(重要)

    原文:分享非常有用的Java程序(关键代码)(八)---Java InputStream读取网络响应Response数据的方法!(重要) Java InputStream读取数据问题 ======== ...

  6. Java ftp上传文件方法效率对比

    Java ftp上传文件方法效率对比 一.功能简介: txt文件采用ftp方式从windows传输到Linux系统: 二.ftp实现方法 (1)方法一:采用二进制流传输,设置缓冲区,速度快,50M的t ...

  7. 【转】使用JavaParser获得Java代码中的类名、方法形参列表中的参数名以及统计总的文件个数与不能解析的文件个数

    遍历目录查找Java文件: public static void ergodicDir(File dir, HashSet<String> argNameSet, HashSet<S ...

  8. 019 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 13 数据类型转换的代码示例

    019 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 13 数据类型转换的代码示例 本文知识点:Java中的数据类型转换案例 学习视频有误,导致没法写文,文章内容 ...

  9. 101 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 05 通过方法实现学生类与专业类关联——方案二

    101 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 05 通过方法实现学生类与专业类关联--方案二 本文知识点:通过方法实现学生类与 ...

随机推荐

  1. 源码分析-Producer

    消息生产者的代码都在client模块中,相对于RocketMQ来讲,消息生产者就是客户端,也是消息的提供者. 方法和属性 主要方法介绍 //创建主题 void createTopic(final St ...

  2. 内存管理——placement new

    C++给我们三个申请内存的方式,new(new operator),array new 和placement new. placement new意思是 让对象构建在已经分配好的内存上. (这里我再把 ...

  3. Linux shell实现每天定时备份mysql数据库

    每天定时备份mysql数据库任务,删除指定天数前的数据,保留指定天的数据: 需求: 1,每天4点备份mysql数据: 2,为节省空间,删除超过3个月的所有备份数据: 3,删除超过7天的备份数据,保留3 ...

  4. zookeeper 异常 :stat is not executed because it is not in the whitelist. Connection closed b

    1 .问题 1.启动 zookeeper 后 用指令:  telnet 127.0.0.1 2181 连接 提示输入指令 :stat 后报错,然后关闭连接 2.问题解决: 修改启动指令 zkServe ...

  5. SpringCloud微服务-Eureka服务注册与发现

    一. Eureka 是什么? Eureka是Netflix的一个子模块,也是核心模块之一.Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移.服务注册与发现对微服务 ...

  6. 修复Apache Log4j任意代码执行漏洞安全风险通告

    2021年12月10日 0x01漏洞背景 Apache Log4j 是 Apache 的一个开源项目,Apache Log4j2是一个基于Java的日志记录工具.该工具重写了Log4j框架,并且引入了 ...

  7. python爬虫期末复习

    python期末复习 选择题 以下选项中合法的是(A). A 爬取百度的搜索结果 B 爬取淘宝的商品数据 C 出售同学的个人信息 D 为高利贷提供技术服务 网站的根目录下有一个文件告诉爬虫哪些内容可以 ...

  8. Python用xlrd读取Excel数据到list中再用xlwt把数据写入到新的Excel中

    一.先用xlrd读取Excel数据到list列表中(存入列表中的数据如下图所示) import xlrd as xd #导入需要的包 import xlwt data =xd.open_workboo ...

  9. HUST-计算机网络实验-socket编程

    随笔---HUST计网实验:socket编程 博主大三在读,第一次写随笔,水平有限,就当记录一下学习的过程,顺便面试前复习项目的时候看看. 实验要求: 编写一个 Web 服务器软件,要求如下: 基本要 ...

  10. .NET6中一些常用组件的配置及使用记录,持续更新中。。。

    NET6App 介绍 .NET 6的CoreApp框架,用来学习.NET6的一些变动和新特性,使用EFCore,等一系列组件的运用,每个用单独的文档篇章记录,持续更新文档哦. 如果对您有帮助,点击右上 ...