diff --git a/build.gradle b/build.gradle index 60f76f3..e4d4f7c 100644 --- a/build.gradle +++ b/build.gradle @@ -61,6 +61,6 @@ uploadArchives { // 项目名称 pom.artifactId = "OkReflect" // 版本号 - pom.version = "0.0.6" + pom.version = "0.1.3" } } diff --git a/src/main/kotlin/okreflect/MethodGetter.kt b/src/main/kotlin/okreflect/MethodGetter.kt index 8ce0c56..1db8be1 100644 --- a/src/main/kotlin/okreflect/MethodGetter.kt +++ b/src/main/kotlin/okreflect/MethodGetter.kt @@ -29,10 +29,10 @@ class MethodGetter { * * Get the classes of arguments cof constructor or method of the class. */ - private fun getParametersType(args: Array): Array?> { + private fun getParametersType(args: Array): Array?> { val result = arrayOfNulls>(args.size) for (i in args.indices) { - result[i] = args[i]::class.java + result[i] = args[i]!!::class.java } return result } @@ -42,13 +42,13 @@ class MethodGetter { * * Get the classes of arguments cof constructor or method of the class. */ - private fun getConversedParametersType(args: Array): Array?> { + private fun getConversedParametersType(args: Array): Array?> { val result = arrayOfNulls>(args.size) for (i in args.indices) { - // When the parameters type is int, Kotlin will take it as Integer, - // so I specify the type as primitive type, + // When the parameter type is primitive, Kotlin will box it, + // so I specify the type as primitive type manually, // I have not found another solution to solve this problem , if you have - // any idea or suggestion, you can contact me. + // any idea or suggestion, please tell me, thanks. result[i] = when (args[i]) { is Byte -> Byte::class.java is Short -> Short::class.java @@ -58,33 +58,39 @@ class MethodGetter { is Float -> Float::class.java is Double -> Double::class.java is Boolean -> Boolean::class.java - else -> args[i]::class.java + else -> args[i]!!::class.java } } return result } /** - * + * @param clazz: The class that you want to use. * @param name: The name of the method you want to call. * @param args: Parameters that use to call the method. + * @param parameterTypes: The class of parameters in the method. * - * Get method by the method name you've passed. + * Get method by the name and parameters of the method you've passed. */ - fun getMethod(clazz: Class<*>?, name: String, args: Array): Method? { + fun getMethod( + clazz: Class<*>?, + name: String, + args: Array, + parameterTypes: Array>? + ): Method? { var exception: Exception? = null var method: Method? = null try { - method = getDeclaredMethod(clazz, name, args) + method = getDeclaredMethod(clazz, name, args, parameterTypes) } catch (e: Exception) { exception = e } if (method == null) { try { - method = getNonDeclaredMethod(clazz, name, args) + method = getNonDeclaredMethod(clazz, name, args, parameterTypes) } catch (e: Exception) { exception = e } @@ -104,12 +110,17 @@ class MethodGetter { /** * Get method. */ - private fun getNonDeclaredMethod(clazz: Class<*>?, name: String, args: Array): Method? { + private fun getNonDeclaredMethod( + clazz: Class<*>?, + name: String, + args: Array, + parameterTypes: Array>? + ): Method? { var exception: Exception? = null var method: Method? = null try { - val types = getParametersType(args) + val types = parameterTypes ?: getParametersType(args) method = clazz!!.getMethod(name, *types) } catch (e: Exception) { exception = e @@ -117,7 +128,7 @@ class MethodGetter { if (method == null) { try { - val types = getConversedParametersType(args) + val types = parameterTypes ?: getConversedParametersType(args) method = clazz!!.getMethod(name, *types) } catch (e: Exception) { exception = e @@ -132,11 +143,16 @@ class MethodGetter { /** * Get declared method. */ - private fun getDeclaredMethod(clazz: Class<*>?, name: String, args: Array): Method? { + private fun getDeclaredMethod( + clazz: Class<*>?, + name: String, + args: Array, + parameterTypes: Array>? + ): Method? { var exception: Exception? = null var declared: Method? = null try { - val types = getParametersType(args) + val types = parameterTypes ?: getParametersType(args) declared = clazz!!.getDeclaredMethod(name, *types) } catch (e: Exception) { exception = e @@ -144,7 +160,7 @@ class MethodGetter { if (declared == null) { try { - val types = getConversedParametersType(args) + val types = parameterTypes ?: getConversedParametersType(args) declared = clazz!!.getDeclaredMethod(name, *types) } catch (e: Exception) { exception = e diff --git a/src/main/kotlin/okreflect/OkReflect.kt b/src/main/kotlin/okreflect/OkReflect.kt index ed08a0b..5e13e9f 100644 --- a/src/main/kotlin/okreflect/OkReflect.kt +++ b/src/main/kotlin/okreflect/OkReflect.kt @@ -76,6 +76,11 @@ class OkReflect { */ private var withOuterInstance = false + /** + * The class of parameters in the method. + */ + private var parameterTypes: Array>? = null + /** * @param className: The name of the class that you want to create. * @@ -154,13 +159,22 @@ class OkReflect { } /** - * @param methodName: the name of the method that you want to call. + * @param methodName: The name of the method that you want to call. + * @param classes: The class of the parameters. * @param args: The parameters of the method that you wan to call. * * Call the method that you want to call. * The method will be called when [get] method called. * The method will be call with the instance. */ + fun call(methodName: String, classes: Array>, vararg args: Any): OkReflect { + parameterTypes = classes + return realCall(methodName, true, *args) + } + + /** + * @See [call] + */ fun call(methodName: String, vararg args: Any): OkReflect { return realCall(methodName, true, *args) } @@ -202,6 +216,7 @@ class OkReflect { * you need to pass the instance in this method. */ fun with(instance: Any): OkReflect { + withOuterInstance = true this.instance = instance return this } @@ -218,6 +233,7 @@ class OkReflect { return this } + /** * @param fieldName: The name of the field. * @param arg: The value that you want to set to the field. @@ -241,7 +257,7 @@ class OkReflect { */ private fun setFieldOfInstance(fieldName: String, arg: Any) { val osName = System.getProperty("os.name") - var field = instance!!.javaClass.getDeclaredField(fieldName) + var field = clazz!!.getDeclaredField(fieldName) field = accessible(field) if (osName != "Linux") { removeFinalModifier(field) @@ -263,14 +279,12 @@ class OkReflect { */ private fun invoke(methodCall: MethodCall) { val args = methodCall.args - val methods = instance!!.javaClass.methods - val method = getMethod(clazz, methodCall.methodName, args) - val withInstance = methodCall.callWithInstance + val method = getMethod(clazz, methodCall.methodName, args, parameterTypes) val returnType = method!!.returnType.toString() if (returnType == "void") { method.invoke(instance, *args) } else { - result = if (withInstance) { + result = if (methodCall.callWithInstance) { method.invoke(instance, *args) } else { verifyResult() @@ -376,25 +390,32 @@ class OkReflect { } } - /** - * Get the result value from last method if it has a return value, - * or else you will get the instance. - */ - fun get(): T? { - return getByFlag(RETURN_FLAG_RESULT) - } - /** * @param fieldName: The name of the field that you want to get. * - * Get the result value from last method if it has a return value, - * or else you will get the instance. + * Get instance when return value from last method is null. */ fun get(fieldName: String): T? { targetFieldName = fieldName return getByFlag(RETURN_FLAG_FIELD) } + /** + * @see [get] + */ + fun get(): T? { + return getByFlag(RETURN_FLAG_RESULT_OR_INSTANCE) + } + + /** + * OkReflect will return instance when the result is null, + * when you trying to get return value no matter result is null, + * then you can use this method. + */ + fun getResult():T? { + return getByFlag(RETURN_FLAG_RESULT) + } + /** * Get the class. */ @@ -427,14 +448,11 @@ class OkReflect { */ private fun realGet(returnFlag: Int): T? { if (!withOuterInstance) { - val needInstance = returnFlag == RETURN_FLAG_INSTANCE || returnFlag == RETURN_FLAG_RESULT - if (needInstance) { + if (needInstance(returnFlag)) { verifyClassInfo() verifyConstructorArgs() } - if (clazz == null) { - this.clazz = Class.forName(className!!) - } + initClazz() if (createCalled) { initInstance() invokeMethods() @@ -447,6 +465,16 @@ class OkReflect { return getByResult(returnFlag) } + private fun initClazz() { + if (clazz == null) { + this.clazz = Class.forName(className!!) + } + } + + private fun needInstance(returnFlag: Int): Boolean { + return returnFlag == RETURN_FLAG_INSTANCE || returnFlag == RETURN_FLAG_RESULT_OR_INSTANCE + } + /** * If there is no constructor parameters for the , there will throw an exception */ @@ -495,7 +523,8 @@ class OkReflect { RETURN_FLAG_FIELD -> { targetFieldValue as T } - RETURN_FLAG_RESULT -> { + RETURN_FLAG_RESULT -> result as T + RETURN_FLAG_RESULT_OR_INSTANCE -> { if (result != null) { result as T } else { @@ -526,13 +555,18 @@ class OkReflect { /** * Return the return value from the method that you invoked. */ - private const val RETURN_FLAG_RESULT = 3 + private const val RETURN_FLAG_RESULT_OR_INSTANCE = 3 /** * Return the field. */ private const val RETURN_FLAG_FIELD = 4 + /** + * Return the return value from the invoked method. + */ + private const val RETURN_FLAG_RESULT = 5 + /** * Set the class name of the instance/ */ diff --git a/src/test/java/TestClass.java b/src/test/java/TestClass.java index e1c3ca6..2d80065 100644 --- a/src/test/java/TestClass.java +++ b/src/test/java/TestClass.java @@ -7,6 +7,7 @@ public class TestClass extends SuperTestClass { private static final String staticFinalField = "finalString"; private final String nickname = "666"; private byte b; + private Byte b2; public char c = 'a'; private static int i = 10; private Integer i2 = 12; @@ -25,6 +26,10 @@ private TestClass(String name, int age) { this.age = age; } + private void setName(String name) { + this.name = name; + } + private String getName() { return name; } @@ -42,4 +47,9 @@ private void setData(String name, byte b) { this.b = b; } + private void setData2(String name, Byte b) { + this.name = name; + this.b2 = b; + } + } diff --git a/src/test/java/UseCaseTest.java b/src/test/java/UseCaseTest.java index c1d6383..8359b6f 100644 --- a/src/test/java/UseCaseTest.java +++ b/src/test/java/UseCaseTest.java @@ -8,6 +8,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -234,7 +235,24 @@ public void testCallMethodWithMultipleParameter() { assert name.equals("Tom"); } + @Test + public void testCallMethodWithVoidParameter() { + TestClass testClass = new TestClass(); + Class classes[] = {String.class, Byte.class}; + String name = OkReflect.on(testClass) + .call("setData2", classes, "Tom", null) + .get("name"); + assert name.equals("Tom"); + } + @Test + public void testGetResult() { + TestClass testClass = new TestClass(); + String name = OkReflect.on(testClass) + .call("getName") + .getResult(); + assert name.equals("default"); + } @Ignore @Test @@ -245,5 +263,10 @@ public void testSetFinalFieldOfClass() { assert finalField.equals("changed"); } + // Fix Bug 'set field of instance failed, when class is super class', + // Fix Bug 'get class of parameter failed, when parameter is null' + // Added getResult() method for the purpose of obtain the return value no matter it is null or not. + // Added classes parameter to call() and simpleCall() methods for the purpose of + // passing void parameter into the method. }