gpt4 book ai didi

java - 使用 MethodHandle 查找最具体的重载方法

转载 作者:行者123 更新时间:2023-12-04 16:03:33 27 4
gpt4 key购买 nike

假设我在给定类型(类/接口(interface))中有三个方法:

public void foo(Integer integer);
public void foo(Number number);
public void foo(Object object);

使用 MethodHandle或反射,我想为仅在运行时知道类型的对象找到最具体的重载方法。即我想做 JLS 15.12在运行时。

例如,假设我在包含这三种方法的上述类型的方法中有以下内容:
Object object = getLong(); // runtime type is Long *just an example*

MethodHandles.lookup()
.bind(this, "foo", methodType(Void.class, object.getClass()))
.invoke(object);

然后我在概念上会想要 foo(Number number)被选择,但上面会抛出一个异常,因为 API 只会寻找 foo(Long)方法,仅此而已。注意 Long 的用法这里只是一个例子。对象的类型实际上可以是任何东西; String, MyBar, Integer, ..., 等等等等。

MethodHandle API 中是否有某些东西可以在运行时自动执行与编译器在 JLS 15.12 之后执行的相同类型的解析?

最佳答案

基本上我搜索了所有可以使用一组参数执行的方法。因此,我按照 parameterType 与 methodParameterType 之间的距离对它们进行了排序。这样做,我可以获得最具体的重载方法。

测试:

@Test
public void test() throws Throwable {
Object object = 1;

Foo foo = new Foo();

MethodExecutor.execute(foo, "foo", Void.class, object);
}

富:
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");
}
}

MethodExecutor:
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);
//noinspection unchecked
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);
}
}

当然可以通过一些重构和注释来改进它。

关于java - 使用 MethodHandle 查找最具体的重载方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58581450/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com