littlerookie 发表于 2023-7-26 22:22

类泛型工具类 - 【【【只作用于父类(非接口)有泛型的类】】】

本帖最后由 littlerookie 于 2023-7-27 09:23 编辑

```
package xxx.xxx.xxx;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
* @Description: 类泛型工具类 - 【【【只作用于父类(非接口)有泛型的类】】】
*/
@Slf4j
public enum ClassGenericTypeUtil {

    ;

    /**
   * @param sort 泛型序号值 从0开始
   * @Param clazz 获取类的class
   * @description 获取指定顺序泛型类型 - 不可以获取嵌套层级的泛型
   */
    public static Class getGenericType(Class<?> clazz, int sort) {
      try {
            if (Objects.isNull(clazz)) {
                return null;
            }
            Type superclass = clazz.getGenericSuperclass();
            if (superclass instanceof Class) {
                throw new IllegalArgumentException("内部错误:在没有实际类型信息的情况下构造getGenericType");
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) superclass;
                Type[] typeList = parameterizedType.getActualTypeArguments();
                String typeName = typeList.getTypeName();
                // <> 必定是成对出现的
                if (typeName.contains(SymbolConstants.Symbol.En.LEFT_ANGLE_BRACKETS)) {
                  typeName = typeName.split(SymbolConstants.Symbol.En.COMMA);
                }
                return Class.forName(typeName);
            }
      } catch (Exception e) {
            log.error("【获取泛型出现异常】:", e);
      }
      return null;
    }

    /**
   * @param type 泛型类型实例
   * @description 泛型类型实例 获取 泛型树形结构
   */
    public static List<GenericNode> getGenericNodeTree(Type type) {
      return ClassGenericTypeUtil.genericNodeList(type.getTypeName(), Boolean.FALSE);
    }

    /**
   * @param genericTypeName 泛型类型字符串
   * @description 泛型类型字符串 获取 泛型树形结构
   * genericTypeName可能的复杂示例:
   * List<Map<Map<String, Function<String, Object>>, Map<String, String>>>
   * java.util.List<java.util.Map<java.util.Map<java.lang.String, java.util.function.Function<java.lang.String, java.lang.Object>>, java.util.Map<java.lang.String, java.lang.String>>>
   */
    public static List<GenericNode> getGenericNodeTree(String genericTypeName) {
      return ClassGenericTypeUtil.genericNodeList(genericTypeName, Boolean.FALSE);
    }

    /**
   * @param genericTypeName 泛型类型字符串
   * @description 校验泛型类型字符串是否符合要求,【目前暂无有效规则判断】,所以默认返回true
   * genericTypeName可能的复杂示例:
   * List<Map<Map<String, Function<String, Object>>, Map<String, String>>>
   * java.util.List<java.util.Map<java.util.Map<java.lang.String, java.util.function.Function<java.lang.String, java.lang.Object>>, java.util.Map<java.lang.String, java.lang.String>>>
   */
    private static Boolean validGenericTypeName(String genericTypeName) {
      return Boolean.TRUE;
    }

    /**
   * @param genericTypeName 泛型类型字符串
   * @param innerInvoke   是否内部调用 true 内部调用 false 非内部调用
   * @description 泛型类型字符串 获取 泛型树形结构
   * genericTypeName可能的复杂示例:
   * List<Map<Map<String, Function<String, Object>>, Map<String, String>>>
   * java.util.List<java.util.Map<java.util.Map<java.lang.String, java.util.function.Function<java.lang.String, java.lang.Object>>, java.util.Map<java.lang.String, java.lang.String>>>
   */
    private static List<GenericNode> genericNodeList(String genericTypeName, Boolean innerInvoke) {
      if (innerInvoke == null || Boolean.FALSE.equals(innerInvoke)) {
            //只会在首次调用的时候校验,之后就不会校验了
            innerInvoke = ClassGenericTypeUtil.validGenericTypeName(genericTypeName);
      }
      return ClassGenericTypeUtil.genericNodeList(genericTypeName, Boolean.FALSE, innerInvoke);
    }

    /**
   * @param genericTypeName   泛型类型字符串
   * @param parseParallelNode 是否解析平行节点 true 解析平行节点 false 不解析平行节点 (genericTypeName 有可能为【java.lang.String, java.lang.Object】)
   * @param innerInvoke       是否内部调用 true 内部调用 false 非内部调用
   * @description 泛型类型字符串 获取 泛型树形结构
   * genericTypeName可能的复杂示例:
   * List<Map<Map<String, Function<String, Object>>, Map<String, String>>>
   * java.util.List<java.util.Map<java.util.Map<java.lang.String, java.util.function.Function<java.lang.String, java.lang.Object>>, java.util.Map<java.lang.String, java.lang.String>>>
   */
    private static List<GenericNode> genericNodeList(String genericTypeName, Boolean parseParallelNode, Boolean innerInvoke) {
      if (StrUtil.isBlank(genericTypeName)) {
            return Collections.emptyList();
      }
      if (ObjectUtil.isNull(innerInvoke) || Boolean.FALSE.equals(innerInvoke)) {
            //只会在首次调用的时候校验,之后就不会校验了
            innerInvoke = ClassGenericTypeUtil.validGenericTypeName(genericTypeName);
      }
      if (ObjectUtil.isNull(parseParallelNode)) {
            parseParallelNode = false;
      }
      //genericTypeName 可能场景如下
      //java.util.List<java.util.Map<java.util.Map<java.lang.String, java.util.function.Function<java.lang.String, java.lang.Object>>, java.util.Map<java.lang.String, java.lang.String>>>
      List<GenericNode> resultList = CollUtil.newArrayList();
      try {
            //基于 <> 必会成对出现前提
            //含 < 即表示至少有一个子节点
            if (genericTypeName.contains(SymbolConstants.Symbol.En.LEFT_ANGLE_BRACKETS) && genericTypeName.contains(SymbolConstants.Symbol.En.RIGHT_ANGLE_BRACKETS)) {
                //parseParallelNode = false 表示 是否解析平行节点 即 java.lang.String, java.lang.Object 场景
                if (!genericTypeName.contains(SymbolConstants.Symbol.En.COMMA) || !parseParallelNode) {
                  GenericNode currentNode = new GenericNode();
                  //先去除最外层的 <>
                  String currentClassName = genericTypeName.substring(0, genericTypeName.indexOf(SymbolConstants.Symbol.En.LEFT_ANGLE_BRACKETS));
                  //获取当前节点的 Class 实例
                  currentNode.setClazz(Class.forName(currentClassName));
                  //获取子节点的 Class 实例集
                  //replace 为 <....> 中的内容
                  String childNode = genericTypeName.substring(genericTypeName.indexOf(SymbolConstants.Symbol.En.LEFT_ANGLE_BRACKETS) + 1, genericTypeName.lastIndexOf(SymbolConstants.Symbol.En.RIGHT_ANGLE_BRACKETS));
                  //子节点必须要校验是否有平行节点
                  currentNode.setNodeList(ClassGenericTypeUtil.genericNodeList(childNode, innerInvoke, Boolean.TRUE));
                  resultList.add(currentNode);
                } else {
                  //解析 genericTypeName 中的泛型类型
                  String[] genericTypeNameSplit = genericTypeName.split(SymbolConstants.Symbol.En.COMMA);
                  List<String> nodeStringList = CollUtil.newArrayList();
                  StringBuilder stringBuilder = new StringBuilder();
                  boolean first = true;
                  for (String childGenericTypeName : genericTypeNameSplit) {
                        if (!first) {
                            stringBuilder.append(SymbolConstants.Symbol.En.COMMA);
                        } else {
                            first = false;
                        }
                        stringBuilder.append(childGenericTypeName);
                        if (!stringBuilder.toString().contains(SymbolConstants.Symbol.En.LEFT_ANGLE_BRACKETS) ||
                              StrUtil.count(stringBuilder.toString(), SymbolConstants.Symbol.En.LEFT_ANGLE_BRACKETS) == StrUtil.count(stringBuilder.toString(), SymbolConstants.Symbol.En.RIGHT_ANGLE_BRACKETS)) {
                            String trimString = stringBuilder.toString().trim();
                            if (StrUtil.isNotBlank(trimString)) {
                              nodeStringList.add(trimString);
                            }
                            //清空 stringBuilder 内容
                            stringBuilder.delete(0, stringBuilder.length());
                            first = true;
                        }
                  }
                  //将 genericTypeName 拆分成多个 genericTypeName 然后递归调用 此方法
                  if (CollUtil.isEmpty(nodeStringList)) {
                        return resultList;
                  }
                  final Boolean innerInvokeFinal = innerInvoke;
                  nodeStringList.forEach(nodeString -> {
                        //nodeString 由上述赋值可知 必定是有效值, 是一个具体确定的类型 但是可能也含泛型 返回的数据 不为空的话必定有有且仅有一条数据
                        //示例:java.util.Map<java.util.Map<java.lang.String, java.util.function.Function<java.lang.String, java.lang.Object>>>
                        List<GenericNode> nodeList = ClassGenericTypeUtil.genericNodeList(nodeString, innerInvokeFinal);
                        if (CollUtil.isEmpty(nodeList)) {
                            return;
                        }
                        resultList.add(nodeList.get(0));
                  });
                }
            } else if (genericTypeName.contains(SymbolConstants.Symbol.En.COMMA)) {
                //有可能 genericTypeName = java.lang.String, java.lang.String类型的字符串
                String[] genericTypeNameSplit = genericTypeName.split(SymbolConstants.Symbol.En.COMMA);
                for (String genericTypeNameParallel : genericTypeNameSplit) {
                  List<GenericNode> nodeList = ClassGenericTypeUtil.genericNodeList(genericTypeNameParallel.trim(), innerInvoke);
                  if (CollUtil.isEmpty(nodeList)) {
                        continue;
                  }
                  resultList.add(nodeList.get(0));
                }
            } else {
                resultList.add(new GenericNode().setClazz(Class.forName(genericTypeName)));
            }
      } catch (Exception e) {
            log.error("出现异常:", e);
      }
      return resultList;
    }

    @Data
    @Accessors(chain = true)
    public static class GenericNode implements Serializable {

      /**
         * 当前节点的泛型的class类型
         */
      private Class<?> clazz;

      /**
         * 子泛型的泛型类型集
         */
      private List<GenericNode> nodeList;

      /**
         * @description 将此节点及其子节点的树形结构转换成一个节点Set集合 - 去重
         */
      public Set<GenericNode> treeToSet() {
            Set<GenericNode> resultList = CollUtil.newHashSet();
            if (CollUtil.isEmpty(this.nodeList)) {
                resultList.add(this);
            } else {
                this.nodeList.forEach(node -> resultList.addAll(node.treeToSet()));
            }
            return resultList;
      }

      /**
         * @description 将此节点及其子节点的树形结构转换成一个节点List集合 - 去重
         */
      public List<GenericNode> treeToList() {
            return CollUtil.newArrayList(this.treeToSet());
      }

    }

    public interface SymbolConstants {
      interface Symbol {
            public interface Cn {
                String COMMA = ",";
                String COLON = ":";
            }

            interface En {
                String COMMA = ",";
                String COLON = ":";
                String HYPHEN = "-";
                String UNDERLINE = "_";
                String LEFT_ANGLE_BRACKETS = "<";
                String RIGHT_ANGLE_BRACKETS = ">";
            }
      }
    }

}
```

youxiaxy 发表于 2023-7-27 08:11

感谢分享。学习一下。

pjy612 发表于 2023-7-27 09:10

拿代码段 套一下比较好

littlerookie 发表于 2023-7-27 09:24

pjy612 发表于 2023-7-27 09:10
拿代码段 套一下比较好

弄好了,之前一直没找到在哪{:1_907:}

qeadzc 发表于 2023-7-30 23:44

感谢楼主分享

harglo 发表于 2023-8-3 09:49

请问该工具类有什么应用场景,可以简单说一下作用吗
页: [1]
查看完整版本: 类泛型工具类 - 【【【只作用于父类(非接口)有泛型的类】】】