基本上,我搜索了所有可以执行的带有一组参数的方法。因此,我按照参数类型与方法参数类型之间的距离对它们进行了排序。通过这样做,我可以获得最特定的重载方法。
测试方法:
@Test
public void test() throws Throwable {
Object object = 1;
Foo foo = new Foo();
MethodExecutor.execute(foo, "foo", Void.class, object);
}
The Foo:
class Foo {
public void foo(Integer integer) {
System.out.println("integer");
}
public void foo(Number number) {
System.out.println("number");
}
public void foo(Object object) {
System.out.println("object");
}
}
方法执行器:
public class MethodExecutor{
private static final Map<Class<?>, Class<?>> equivalentTypeMap = new HashMap<>(18);
static{
equivalentTypeMap.put(boolean.class, Boolean.class);
equivalentTypeMap.put(byte.class, Byte.class);
equivalentTypeMap.put(char.class, Character.class);
equivalentTypeMap.put(float.class, Float.class);
equivalentTypeMap.put(int.class, Integer.class);
equivalentTypeMap.put(long.class, Long.class);
equivalentTypeMap.put(short.class, Short.class);
equivalentTypeMap.put(double.class, Double.class);
equivalentTypeMap.put(void.class, Void.class);
equivalentTypeMap.put(Boolean.class, boolean.class);
equivalentTypeMap.put(Byte.class, byte.class);
equivalentTypeMap.put(Character.class, char.class);
equivalentTypeMap.put(Float.class, float.class);
equivalentTypeMap.put(Integer.class, int.class);
equivalentTypeMap.put(Long.class, long.class);
equivalentTypeMap.put(Short.class, short.class);
equivalentTypeMap.put(Double.class, double.class);
equivalentTypeMap.put(Void.class, void.class);
}
public static <T> T execute(Object instance, String methodName, Class<T> returnType, Object ...parameters) throws InvocationTargetException, IllegalAccessException {
List<Method> compatiblesMethods = getCompatiblesMethods(instance, methodName, returnType, parameters);
Method mostSpecificOverloaded = getMostSpecificOverLoaded(compatiblesMethods, parameters);
return (T) mostSpecificOverloaded.invoke(instance, parameters);
}
private static List<Method> getCompatiblesMethods(Object instance, String methodName, Class<?> returnType, Object[] parameters) {
Class<?> clazz = instance.getClass();
Method[] methods = clazz.getMethods();
List<Method> compatiblesMethods = new ArrayList<>();
outerFor:
for(Method method : methods){
if(!method.getName().equals(methodName)){
continue;
}
Class<?> methodReturnType = method.getReturnType();
if(!canBeCast(returnType, methodReturnType)){
continue;
}
Class<?>[] methodParametersType = method.getParameterTypes();
if(methodParametersType.length != parameters.length){
continue;
}
for(int i = 0; i < methodParametersType.length; i++){
if(!canBeCast(parameters[i].getClass(), methodParametersType[i])){
continue outerFor;
}
}
compatiblesMethods.add(method);
}
if(compatiblesMethods.size() == 0){
throw new IllegalArgumentException("Cannot find method.");
}
return compatiblesMethods;
}
private static Method getMostSpecificOverLoaded(List<Method> compatiblesMethods, Object[] parameters) {
Method mostSpecificOverloaded = compatiblesMethods.get(0);
int lastMethodScore = calculateMethodScore(mostSpecificOverloaded, parameters);
for(int i = 1; i < compatiblesMethods.size(); i++){
Method method = compatiblesMethods.get(i);
int currentMethodScore = calculateMethodScore(method, parameters);
if(lastMethodScore > currentMethodScore){
mostSpecificOverloaded = method;
lastMethodScore = currentMethodScore;
}
}
return mostSpecificOverloaded;
}
private static int calculateMethodScore(Method method, Object... parameters){
int score = 0;
Class<?>[] methodParametersType = method.getParameterTypes();
for(int i = 0; i < parameters.length; i++){
Class<?> methodParameterType = methodParametersType[i];
if(methodParameterType.isPrimitive()){
methodParameterType = getEquivalentType(methodParameterType);
}
Class<?> parameterType = parameters[i].getClass();
score += distanceBetweenClasses(parameterType, methodParameterType);
}
return score;
}
private static int distanceBetweenClasses(Class<?> clazz, Class<?> superClazz){
return distanceFromObjectClass(clazz) - distanceFromObjectClass(superClazz);
}
private static int distanceFromObjectClass(Class<?> clazz){
int distance = 0;
while(!clazz.equals(Object.class)){
distance++;
clazz = clazz.getSuperclass();
}
return distance;
}
private static boolean canBeCast(Class<?> fromClass, Class<?> toClass) {
if (canBeRawCast(fromClass, toClass)) {
return true;
}
Class<?> equivalentFromClass = getEquivalentType(fromClass);
return equivalentFromClass != null && canBeRawCast(equivalentFromClass, toClass);
}
private static boolean canBeRawCast(Class<?> fromClass, Class<?> toClass) {
return fromClass.equals(toClass) || !toClass.isPrimitive() && toClass.isAssignableFrom(fromClass);
}
private static Class<?> getEquivalentType(Class<?> type){
return equivalentTypeMap.get(type);
}
}
当然,通过一些重构和注释可以改进它。
object.getClass()
改为Number.class
? - Timothy TruckleNumber.class
。Object
可以是任何类型,Long
只是一个例子。 - Arjan Tijmsobject
的父类... - Timothy Truckle