代码实现
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionGenericClass<T extends Number> {
private T instance;
public ReflectionGenericClass(T instance) {
this.instance = instance;
}
public Object invokeMethodByName(String methodName, Object[] params) {
try {
// 获取T类型的Class对象
Class<? extends Number> clazz = instance.getClass();
// 根据方法名和参数类型获取Method对象
Method method = null;
if (params == null || params.length == 0) {
method = clazz.getMethod(methodName);
} else {
Class<?>[] paramTypes = new Class[params.length];
for (int i = 0; i < params.length; i++) {
paramTypes[i] = params[i].getClass();
}
method = clazz.getMethod(methodName, paramTypes);
}
// 调用方法并返回结果
return method.invoke(instance, params);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
return null;
}
}
}
泛型、反射以及Number类特性的协同工作
- 泛型:通过定义
ReflectionGenericClass<T extends Number>
,确保了传入的类型 T
必须是 Number
类或其子类,这使得代码具有类型安全性,在编译期就能检查类型错误,并且代码可以复用。同时限定 T
为 Number
的子类,使得后续反射操作针对的是具有 Number
特性的类。
- 反射:
invokeMethodByName
方法中使用反射机制。通过 instance.getClass()
获取 T
类型的 Class
对象,这是反射操作的入口。然后根据方法名和参数类型获取 Method
对象,进而使用 method.invoke
调用实例的指定方法。反射机制使得代码可以在运行时动态调用方法,提高了代码的灵活性。
- Number类特性:由于
T
是 Number
的子类,这意味着 T
类型的实例具有 Number
类定义的方法,例如 intValue()
、doubleValue()
等。在反射调用方法时,就可以调用这些 Number
类提供的通用方法,增加了代码的通用性和可预测性。
潜在风险
- 性能问题:反射操作相对直接调用方法性能较低。因为反射需要在运行时解析方法名、参数类型等信息,涉及到动态查找和类型检查,这比编译期绑定的直接方法调用要慢很多。在性能敏感的场景下,频繁使用反射可能会导致性能瓶颈。
- 安全性问题:反射可以访问类的私有方法和字段,如果使用不当,可能会破坏类的封装性,导致程序出现不可预测的行为。例如,在上述代码中,如果传入的
methodName
是一个私有方法名,且程序逻辑依赖于类的封装性,就可能引发问题。
- 类型兼容性问题:虽然泛型确保了
T
是 Number
的子类,但在反射调用时,Object[] params
数组中的参数类型需要与目标方法参数类型严格匹配。如果参数类型不匹配,在运行时会抛出 IllegalArgumentException
。此外,如果 T
类型的子类重写了 Number
类的方法并改变了参数类型,也可能导致反射调用失败。
- NoSuchMethodException:如果传入的
methodName
不存在于 T
类型的实例中,会抛出 NoSuchMethodException
。在实际应用中,需要对这种异常进行适当处理,以避免程序崩溃。
- IllegalAccessException:如果目标方法是私有的,并且没有合适的访问权限(例如未设置
setAccessible(true)
),会抛出 IllegalAccessException
。这也提醒开发者在使用反射时要谨慎处理访问权限问题。