Skip to content

Commit 7c14aec

Browse files
committed
Fix Bug 'set field of instance failed, when class is super class'
1 parent cd4c10a commit 7c14aec

File tree

5 files changed

+125
-42
lines changed

5 files changed

+125
-42
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,6 @@ uploadArchives {
6161
// 项目名称
6262
pom.artifactId = "OkReflect"
6363
// 版本号
64-
pom.version = "0.0.6"
64+
pom.version = "0.1.3"
6565
}
6666
}

src/main/kotlin/okreflect/MethodGetter.kt

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ class MethodGetter {
2929
*
3030
* Get the classes of arguments cof constructor or method of the class.
3131
*/
32-
private fun getParametersType(args: Array<out Any>): Array<Class<*>?> {
32+
private fun getParametersType(args: Array<out Any?>): Array<Class<*>?> {
3333
val result = arrayOfNulls<Class<*>>(args.size)
3434
for (i in args.indices) {
35-
result[i] = args[i]::class.java
35+
result[i] = args[i]!!::class.java
3636
}
3737
return result
3838
}
@@ -42,13 +42,13 @@ class MethodGetter {
4242
*
4343
* Get the classes of arguments cof constructor or method of the class.
4444
*/
45-
private fun getConversedParametersType(args: Array<out Any>): Array<Class<*>?> {
45+
private fun getConversedParametersType(args: Array<out Any?>): Array<Class<*>?> {
4646
val result = arrayOfNulls<Class<*>>(args.size)
4747
for (i in args.indices) {
48-
// When the parameters type is int, Kotlin will take it as Integer,
49-
// so I specify the type as primitive type,
48+
// When the parameter type is primitive, Kotlin will box it,
49+
// so I specify the type as primitive type manually,
5050
// I have not found another solution to solve this problem , if you have
51-
// any idea or suggestion, you can contact me.
51+
// any idea or suggestion, please tell me, thanks.
5252
result[i] = when (args[i]) {
5353
is Byte -> Byte::class.java
5454
is Short -> Short::class.java
@@ -58,33 +58,39 @@ class MethodGetter {
5858
is Float -> Float::class.java
5959
is Double -> Double::class.java
6060
is Boolean -> Boolean::class.java
61-
else -> args[i]::class.java
61+
else -> args[i]!!::class.java
6262
}
6363
}
6464
return result
6565
}
6666

6767
/**
68-
*
68+
* @param clazz: The class that you want to use.
6969
* @param name: The name of the method you want to call.
7070
* @param args: Parameters that use to call the method.
71+
* @param parameterTypes: The class of parameters in the method.
7172
*
72-
* Get method by the method name you've passed.
73+
* Get method by the name and parameters of the method you've passed.
7374
*/
74-
fun getMethod(clazz: Class<*>?, name: String, args: Array<out Any>): Method? {
75+
fun getMethod(
76+
clazz: Class<*>?,
77+
name: String,
78+
args: Array<out Any?>,
79+
parameterTypes: Array<Class<*>>?
80+
): Method? {
7581

7682
var exception: Exception? = null
7783
var method: Method? = null
7884

7985
try {
80-
method = getDeclaredMethod(clazz, name, args)
86+
method = getDeclaredMethod(clazz, name, args, parameterTypes)
8187
} catch (e: Exception) {
8288
exception = e
8389
}
8490

8591
if (method == null) {
8692
try {
87-
method = getNonDeclaredMethod(clazz, name, args)
93+
method = getNonDeclaredMethod(clazz, name, args, parameterTypes)
8894
} catch (e: Exception) {
8995
exception = e
9096
}
@@ -104,20 +110,25 @@ class MethodGetter {
104110
/**
105111
* Get method.
106112
*/
107-
private fun getNonDeclaredMethod(clazz: Class<*>?, name: String, args: Array<out Any>): Method? {
113+
private fun getNonDeclaredMethod(
114+
clazz: Class<*>?,
115+
name: String,
116+
args: Array<out Any?>,
117+
parameterTypes: Array<Class<*>>?
118+
): Method? {
108119
var exception: Exception? = null
109120
var method: Method? = null
110121

111122
try {
112-
val types = getParametersType(args)
123+
val types = parameterTypes ?: getParametersType(args)
113124
method = clazz!!.getMethod(name, *types)
114125
} catch (e: Exception) {
115126
exception = e
116127
}
117128

118129
if (method == null) {
119130
try {
120-
val types = getConversedParametersType(args)
131+
val types = parameterTypes ?: getConversedParametersType(args)
121132
method = clazz!!.getMethod(name, *types)
122133
} catch (e: Exception) {
123134
exception = e
@@ -132,19 +143,24 @@ class MethodGetter {
132143
/**
133144
* Get declared method.
134145
*/
135-
private fun getDeclaredMethod(clazz: Class<*>?, name: String, args: Array<out Any>): Method? {
146+
private fun getDeclaredMethod(
147+
clazz: Class<*>?,
148+
name: String,
149+
args: Array<out Any?>,
150+
parameterTypes: Array<Class<*>>?
151+
): Method? {
136152
var exception: Exception? = null
137153
var declared: Method? = null
138154
try {
139-
val types = getParametersType(args)
155+
val types = parameterTypes ?: getParametersType(args)
140156
declared = clazz!!.getDeclaredMethod(name, *types)
141157
} catch (e: Exception) {
142158
exception = e
143159
}
144160

145161
if (declared == null) {
146162
try {
147-
val types = getConversedParametersType(args)
163+
val types = parameterTypes ?: getConversedParametersType(args)
148164
declared = clazz!!.getDeclaredMethod(name, *types)
149165
} catch (e: Exception) {
150166
exception = e

src/main/kotlin/okreflect/OkReflect.kt

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ class OkReflect {
7676
*/
7777
private var withOuterInstance = false
7878

79+
/**
80+
* The class of parameters in the method.
81+
*/
82+
private var parameterTypes: Array<Class<*>>? = null
83+
7984
/**
8085
* @param className: The name of the class that you want to create.
8186
*
@@ -154,13 +159,22 @@ class OkReflect {
154159
}
155160

156161
/**
157-
* @param methodName: the name of the method that you want to call.
162+
* @param methodName: The name of the method that you want to call.
163+
* @param classes: The class of the parameters.
158164
* @param args: The parameters of the method that you wan to call.
159165
*
160166
* Call the method that you want to call.
161167
* The method will be called when [get] method called.
162168
* The method will be call with the instance.
163169
*/
170+
fun call(methodName: String, classes: Array<Class<*>>, vararg args: Any): OkReflect {
171+
parameterTypes = classes
172+
return realCall(methodName, true, *args)
173+
}
174+
175+
/**
176+
* @See [call]
177+
*/
164178
fun call(methodName: String, vararg args: Any): OkReflect {
165179
return realCall(methodName, true, *args)
166180
}
@@ -202,6 +216,7 @@ class OkReflect {
202216
* you need to pass the instance in this method.
203217
*/
204218
fun with(instance: Any): OkReflect {
219+
withOuterInstance = true
205220
this.instance = instance
206221
return this
207222
}
@@ -218,6 +233,7 @@ class OkReflect {
218233
return this
219234
}
220235

236+
221237
/**
222238
* @param fieldName: The name of the field.
223239
* @param arg: The value that you want to set to the field.
@@ -241,7 +257,7 @@ class OkReflect {
241257
*/
242258
private fun setFieldOfInstance(fieldName: String, arg: Any) {
243259
val osName = System.getProperty("os.name")
244-
var field = instance!!.javaClass.getDeclaredField(fieldName)
260+
var field = clazz!!.getDeclaredField(fieldName)
245261
field = accessible(field)
246262
if (osName != "Linux") {
247263
removeFinalModifier(field)
@@ -263,14 +279,12 @@ class OkReflect {
263279
*/
264280
private fun invoke(methodCall: MethodCall) {
265281
val args = methodCall.args
266-
val methods = instance!!.javaClass.methods
267-
val method = getMethod(clazz, methodCall.methodName, args)
268-
val withInstance = methodCall.callWithInstance
282+
val method = getMethod(clazz, methodCall.methodName, args, parameterTypes)
269283
val returnType = method!!.returnType.toString()
270284
if (returnType == "void") {
271285
method.invoke(instance, *args)
272286
} else {
273-
result = if (withInstance) {
287+
result = if (methodCall.callWithInstance) {
274288
method.invoke(instance, *args)
275289
} else {
276290
verifyResult()
@@ -376,25 +390,32 @@ class OkReflect {
376390
}
377391
}
378392

379-
/**
380-
* Get the result value from last method if it has a return value,
381-
* or else you will get the instance.
382-
*/
383-
fun <T> get(): T? {
384-
return getByFlag(RETURN_FLAG_RESULT)
385-
}
386-
387393
/**
388394
* @param fieldName: The name of the field that you want to get.
389395
*
390-
* Get the result value from last method if it has a return value,
391-
* or else you will get the instance.
396+
* Get instance when return value from last method is null.
392397
*/
393398
fun <T> get(fieldName: String): T? {
394399
targetFieldName = fieldName
395400
return getByFlag<T>(RETURN_FLAG_FIELD)
396401
}
397402

403+
/**
404+
* @see [get]
405+
*/
406+
fun <T> get(): T? {
407+
return getByFlag(RETURN_FLAG_RESULT_OR_INSTANCE)
408+
}
409+
410+
/**
411+
* OkReflect will return instance when the result is null,
412+
* when you trying to get return value no matter result is null,
413+
* then you can use this method.
414+
*/
415+
fun <T> getResult():T? {
416+
return getByFlag(RETURN_FLAG_RESULT)
417+
}
418+
398419
/**
399420
* Get the class.
400421
*/
@@ -427,14 +448,11 @@ class OkReflect {
427448
*/
428449
private fun <T> realGet(returnFlag: Int): T? {
429450
if (!withOuterInstance) {
430-
val needInstance = returnFlag == RETURN_FLAG_INSTANCE || returnFlag == RETURN_FLAG_RESULT
431-
if (needInstance) {
451+
if (needInstance(returnFlag)) {
432452
verifyClassInfo()
433453
verifyConstructorArgs()
434454
}
435-
if (clazz == null) {
436-
this.clazz = Class.forName(className!!)
437-
}
455+
initClazz()
438456
if (createCalled) {
439457
initInstance()
440458
invokeMethods()
@@ -447,6 +465,16 @@ class OkReflect {
447465
return getByResult<T>(returnFlag)
448466
}
449467

468+
private fun initClazz() {
469+
if (clazz == null) {
470+
this.clazz = Class.forName(className!!)
471+
}
472+
}
473+
474+
private fun needInstance(returnFlag: Int): Boolean {
475+
return returnFlag == RETURN_FLAG_INSTANCE || returnFlag == RETURN_FLAG_RESULT_OR_INSTANCE
476+
}
477+
450478
/**
451479
* If there is no constructor parameters for the , there will throw an exception
452480
*/
@@ -495,7 +523,8 @@ class OkReflect {
495523
RETURN_FLAG_FIELD -> {
496524
targetFieldValue as T
497525
}
498-
RETURN_FLAG_RESULT -> {
526+
RETURN_FLAG_RESULT -> result as T
527+
RETURN_FLAG_RESULT_OR_INSTANCE -> {
499528
if (result != null) {
500529
result as T
501530
} else {
@@ -526,13 +555,18 @@ class OkReflect {
526555
/**
527556
* Return the return value from the method that you invoked.
528557
*/
529-
private const val RETURN_FLAG_RESULT = 3
558+
private const val RETURN_FLAG_RESULT_OR_INSTANCE = 3
530559

531560
/**
532561
* Return the field.
533562
*/
534563
private const val RETURN_FLAG_FIELD = 4
535564

565+
/**
566+
* Return the return value from the invoked method.
567+
*/
568+
private const val RETURN_FLAG_RESULT = 5
569+
536570
/**
537571
* Set the class name of the instance/
538572
*/

src/test/java/TestClass.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class TestClass extends SuperTestClass {
77
private static final String staticFinalField = "finalString";
88
private final String nickname = "666";
99
private byte b;
10+
private Byte b2;
1011
public char c = 'a';
1112
private static int i = 10;
1213
private Integer i2 = 12;
@@ -25,6 +26,10 @@ private TestClass(String name, int age) {
2526
this.age = age;
2627
}
2728

29+
private void setName(String name) {
30+
this.name = name;
31+
}
32+
2833
private String getName() {
2934
return name;
3035
}
@@ -42,4 +47,9 @@ private void setData(String name, byte b) {
4247
this.b = b;
4348
}
4449

50+
private void setData2(String name, Byte b) {
51+
this.name = name;
52+
this.b2 = b;
53+
}
54+
4555
}

src/test/java/UseCaseTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.lang.invoke.MethodHandle;
99
import java.lang.invoke.MethodHandles;
1010
import java.lang.reflect.Field;
11+
import java.lang.reflect.InvocationTargetException;
1112
import java.lang.reflect.Method;
1213

1314

@@ -234,7 +235,24 @@ public void testCallMethodWithMultipleParameter() {
234235
assert name.equals("Tom");
235236
}
236237

238+
@Test
239+
public void testCallMethodWithVoidParameter() {
240+
TestClass testClass = new TestClass();
241+
Class classes[] = {String.class, Byte.class};
242+
String name = OkReflect.on(testClass)
243+
.call("setData2", classes, "Tom", null)
244+
.get("name");
245+
assert name.equals("Tom");
246+
}
237247

248+
@Test
249+
public void testGetResult() {
250+
TestClass testClass = new TestClass();
251+
String name = OkReflect.on(testClass)
252+
.call("getName")
253+
.getResult();
254+
assert name.equals("default");
255+
}
238256

239257
@Ignore
240258
@Test
@@ -245,5 +263,10 @@ public void testSetFinalFieldOfClass() {
245263
assert finalField.equals("changed");
246264
}
247265

266+
// Fix Bug 'set field of instance failed, when class is super class',
267+
// Fix Bug 'get class of parameter failed, when parameter is null'
268+
// Added getResult() method for the purpose of obtain the return value no matter it is null or not.
269+
// Added classes parameter to call() and simpleCall() methods for the purpose of
270+
// passing void parameter into the method.
248271

249272
}

0 commit comments

Comments
 (0)