From 9229eea85f77432dca67c8703efc48fd2e4466d5 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 8 Mar 2022 20:52:43 +0800 Subject: [PATCH] fix bug --- CHANGELOG.md | 1 + .../cn/hutool/core/util/ModifierUtil.java | 72 ++++++++++++++----- .../java/cn/hutool/core/util/ReflectUtil.java | 20 ++++-- .../core/lang/test/bean/ExamInfoDict.java | 50 ++----------- .../cn/hutool/core/util/ReflectUtilTest.java | 63 +++++++++++++++- 5 files changed, 134 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd037ca02..3fd0cf5e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### 🐞Bug修复 * 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee) * 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee) +* 【core 】 修复ReflectUtil.getMethods获取接口方法问题(issue#I4WUWR@Gitee) ------------------------------------------------------------------------------------------------------------- # 5.7.22 (2022-03-01) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java index f2b3f389f..dec42bed0 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java @@ -20,35 +20,60 @@ public class ModifierUtil { * @since 4.0.5 */ public enum ModifierType { - /** public修饰符,所有类都能访问 */ + /** + * public修饰符,所有类都能访问 + */ PUBLIC(Modifier.PUBLIC), - /** private修饰符,只能被自己访问和修改 */ + /** + * private修饰符,只能被自己访问和修改 + */ PRIVATE(Modifier.PRIVATE), - /** protected修饰符,自身、子类及同一个包中类可以访问 */ + /** + * protected修饰符,自身、子类及同一个包中类可以访问 + */ PROTECTED(Modifier.PROTECTED), - /** static修饰符,(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类 */ + /** + * static修饰符,(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类 + */ STATIC(Modifier.STATIC), - /** final修饰符,最终修饰符,指定此变量的值不能变,使用在方法上表示不能被重载 */ + /** + * final修饰符,最终修饰符,指定此变量的值不能变,使用在方法上表示不能被重载 + */ FINAL(Modifier.FINAL), - /** synchronized,同步修饰符,在多个线程中,该修饰符用于在运行前,对他所属的方法加锁,以防止其他线程的访问,运行结束后解锁。 */ + /** + * synchronized,同步修饰符,在多个线程中,该修饰符用于在运行前,对他所属的方法加锁,以防止其他线程的访问,运行结束后解锁。 + */ SYNCHRONIZED(Modifier.SYNCHRONIZED), - /** (易失修饰符)指定该变量可以同时被几个线程控制和修改 */ + /** + * (易失修饰符)指定该变量可以同时被几个线程控制和修改 + */ VOLATILE(Modifier.VOLATILE), - /** (过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量,序列化时忽略 */ + /** + * (过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量,序列化时忽略 + */ TRANSIENT(Modifier.TRANSIENT), - /** native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。 */ + /** + * native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。 + */ NATIVE(Modifier.NATIVE), - /** abstract,将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现。 */ + /** + * abstract,将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现。 + */ ABSTRACT(Modifier.ABSTRACT), - /** strictfp,一旦使用了关键字strictfp来声明某个类、接口或者方法时,那么在这个关键字所声明的范围内所有浮点运算都是精确的,符合IEEE-754规范的。 */ + /** + * strictfp,一旦使用了关键字strictfp来声明某个类、接口或者方法时,那么在这个关键字所声明的范围内所有浮点运算都是精确的,符合IEEE-754规范的。 + */ STRICT(Modifier.STRICT); - /** 修饰符枚举对应的int修饰符值 */ + /** + * 修饰符枚举对应的int修饰符值 + */ private final int value; /** * 构造 + * * @param modifier 修饰符int表示,见{@link Modifier} */ ModifierType(int modifier) { @@ -57,6 +82,7 @@ public class ModifierUtil { /** * 获取修饰符枚举对应的int修饰符值,值见{@link Modifier} + * * @return 修饰符枚举对应的int修饰符值 */ public int getValue() { @@ -67,7 +93,7 @@ public class ModifierUtil { /** * 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true) * - * @param clazz 类 + * @param clazz 类 * @param modifierTypes 修饰符枚举 * @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false */ @@ -81,7 +107,7 @@ public class ModifierUtil { /** * 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true) * - * @param constructor 构造方法 + * @param constructor 构造方法 * @param modifierTypes 修饰符枚举 * @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false */ @@ -95,7 +121,7 @@ public class ModifierUtil { /** * 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true) * - * @param method 方法 + * @param method 方法 * @param modifierTypes 修饰符枚举 * @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false */ @@ -109,7 +135,7 @@ public class ModifierUtil { /** * 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true) * - * @param field 字段 + * @param field 字段 * @param modifierTypes 修饰符枚举 * @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false */ @@ -226,15 +252,27 @@ public class ModifierUtil { return clazz.isSynthetic(); } + /** + * 是否抽象方法 + * + * @param method 方法 + * @return 是否抽象方法 + * @since 5.7.23 + */ + public static boolean isAbstract(Method method) { + return hasModifier(method, ModifierType.ABSTRACT); + } //-------------------------------------------------------------------------------------------------------- Private method start + /** * 多个修饰符做“与”操作,表示同时存在多个修饰符 + * * @param modifierTypes 修饰符列表,元素不能为空 * @return “与”之后的修饰符 */ private static int modifiersToInt(ModifierType... modifierTypes) { int modifier = modifierTypes[0].getValue(); - for(int i = 1; i < modifierTypes.length; i++) { + for (int i = 1; i < modifierTypes.length; i++) { modifier |= modifierTypes[i].getValue(); } return modifier; diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java index de406bfbf..4e4a78faf 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java @@ -652,30 +652,36 @@ public class ReflectUtil { } /** - * 获得一个类中所有方法列表,直接反射获取,无缓存 + * 获得一个类中所有方法列表,直接反射获取,无缓存
+ * 接口获取方法和默认方法 * - * @param beanClass 类 - * @param withSuperClassMethods 是否包括父类的方法列表 + * @param beanClass 类或接口 + * @param withSupers 是否包括父类或接口的方法列表 * @return 方法列表 * @throws SecurityException 安全检查异常 */ - public static Method[] getMethodsDirectly(Class beanClass, boolean withSuperClassMethods) throws SecurityException { + public static Method[] getMethodsDirectly(Class beanClass, boolean withSupers) throws SecurityException { Assert.notNull(beanClass); + if(beanClass.isInterface()){ + // 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法 + return withSupers ? beanClass.getMethods() : beanClass.getDeclaredMethods(); + } + Method[] allMethods = null; Class searchType = beanClass; Method[] declaredMethods; - while (searchType != null) { + while (searchType != null && searchType != Object.class) { declaredMethods = searchType.getDeclaredMethods(); if (null == allMethods) { allMethods = declaredMethods; } else { allMethods = ArrayUtil.append(allMethods, declaredMethods); } - searchType = withSuperClassMethods ? searchType.getSuperclass() : null; + searchType = (withSupers && false == searchType.isInterface()) ? searchType.getSuperclass() : null; } - return allMethods; + return ArrayUtil.append(allMethods); } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/test/bean/ExamInfoDict.java b/hutool-core/src/test/java/cn/hutool/core/lang/test/bean/ExamInfoDict.java index a39e91238..b5068f2d8 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/test/bean/ExamInfoDict.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/test/bean/ExamInfoDict.java @@ -1,16 +1,18 @@ package cn.hutool.core.lang.test.bean; +import lombok.Data; + import java.io.Serializable; -import java.util.Objects; /** - * + * * @author 质量过关 * */ +@Data public class ExamInfoDict implements Serializable { private static final long serialVersionUID = 3640936499125004525L; - + // 主键 private Integer id; // 可当作题号 // 试题类型 客观题 0主观题 1 @@ -18,49 +20,7 @@ public class ExamInfoDict implements Serializable { // 试题是否作答 private Integer answerIs; - public Integer getId() { - return id; - } public Integer getId(Integer defaultValue) { return this.id == null ? defaultValue : this.id; } - public void setId(Integer id) { - this.id = id; - } - - public Integer getExamType() { - return examType; - } - public void setExamType(Integer examType) { - this.examType = examType; - } - - public Integer getAnswerIs() { - return answerIs; - } - public void setAnswerIs(Integer answerIs) { - this.answerIs = answerIs; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ExamInfoDict that = (ExamInfoDict) o; - return Objects.equals(id, that.id) && Objects.equals(examType, that.examType) && Objects.equals(answerIs, that.answerIs); - } - - @Override - public int hashCode() { - return Objects.hash(id, examType, answerIs); - } - - @Override - public String toString() { - return "ExamInfoDict{" + "id=" + id + ", examType=" + examType + ", answerIs=" + answerIs + '}'; - } } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java index 9a90c8fe3..bb9dca5f3 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java @@ -116,7 +116,7 @@ public class ReflectUtilTest { @Ignore public void getMethodBenchTest(){ // 预热 - getMethod(TestBenchClass.class, false, "getH"); + getMethodWithReturnTypeCheck(TestBenchClass.class, false, "getH"); final TimeInterval timer = DateUtil.timer(); timer.start(); @@ -127,7 +127,7 @@ public class ReflectUtilTest { timer.restart(); for (int i = 0; i < 100000000; i++) { - getMethod(TestBenchClass.class, false, "getH"); + getMethodWithReturnTypeCheck(TestBenchClass.class, false, "getH"); } Console.log(timer.interval()); } @@ -150,7 +150,7 @@ public class ReflectUtilTest { private String n; } - public static Method getMethod(Class clazz, boolean ignoreCase, String methodName, Class... paramTypes) throws SecurityException { + public static Method getMethodWithReturnTypeCheck(Class clazz, boolean ignoreCase, String methodName, Class... paramTypes) throws SecurityException { if (null == clazz || StrUtil.isBlank(methodName)) { return null; } @@ -169,4 +169,61 @@ public class ReflectUtilTest { } return res; } + + @Test + public void getMethodsFromClassExtends(){ + // 继承情况下,需解决方法去重问题 + final Method[] methods = ReflectUtil.getMethods(C2.class); + Assert.assertEquals(2, methods.length); + } + + @Test + public void getMethodsFromInterfaceTest(){ + // 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法 + // 因此此处得到包括TestInterface1、TestInterface2、TestInterface3中一共4个方法 + final Method[] methods = ReflectUtil.getMethods(TestInterface3.class); + Assert.assertEquals(4, methods.length); + + // 接口里,调用getMethods和getPublicMethods效果相同 + final Method[] publicMethods = ReflectUtil.getPublicMethods(TestInterface3.class); + Assert.assertArrayEquals(methods, publicMethods); + } + + interface TestInterface1{ + void getA(); + void getB(); + + default void getC(){ + + } + } + + interface TestInterface2 extends TestInterface1{ + @Override + void getB(); + } + + interface TestInterface3 extends TestInterface2{ + void get3(); + } + + class C1 implements TestInterface2{ + + @Override + public void getA() { + + } + + @Override + public void getB() { + + } + } + + class C2 extends C1{ + @Override + public void getA() { + + } + } }