在java.lang.reflect.Proxy上实现等于、hashCode和toString

jyztefdp  于 2023-01-01  发布在  Java
关注(0)|答案(1)|浏览(133)

我正在写一个应用程序,需要在运行时实现函数接口,我事先不知道要实现哪些接口,但是可以使用接口的Class对象的反射来解析Method对象。
为了简化问题,假设我正在传递一个包含方法实现的Function对象。
直截了当的东西:

@SuppressWarnings("unchecked")
private <T> T implement(Class<T> interfaceType, Method m, Function<Object[], ?> f) {
    return (T) Proxy.newProxyInstance(
            getClass().getClassLoader(),
            new Class<?>[]{interfaceType},
            (proxy, method, args) -> {
                if (method.equals(m)) {
                    return f.apply(args);
                }

                // Calls to toString, hashCode, and equals go here.
                throw new UnsupportedOperationException(method.getName());
            }
    );
}

因此,对toStringhashCodeequals的调用当前失败,我显然不希望出现这种情况。
java.lang.reflect.Proxydocs开始:
java.lang.Object中声明的hashCodeequalstoString方法在代理示例上的调用将被编码并分派到调用处理程序的invoke方法,其方式与接口方法调用被编码和分派的方式相同。被传递来调用的Method对象的声明类将是java.lang.Object.从java.lang.Object继承的代理示例的其他公共方法不被代理类覆盖,因此这些方法的调用的行为与它们对java.lang.Object的示例所做的一样。
所以我可以随意重写这些方法。这很酷,但我不希望这样。
当然,我可以创建一些虚拟实现来完成或多或少相同的任务(hashCode除外,它是native)或捕获一些其他虚拟对象(可能是调用处理程序本身)并调用那个处理程序上的方法。但是我觉得我应该能够像通常的方式一样“通过”从java.lang.Object继承的方法。或者换句话说,直接在proxy上调用super.hashCode()等的等效项。
有什么好的/标准的方法吗?

nkkqxpd9

nkkqxpd91#

根据评论中的建议,我最终定义了以下基本调用处理程序:

public class ObjectInvocationHandler implements InvocationHandler {
    public static final Method HASH_CODE;
    public static final Method EQUALS;
    public static final Method TO_STRING;
    
    static {
        Class<Object> object = Object.class;
        try {
            HASH_CODE = object.getDeclaredMethod("hashCode");
            EQUALS = object.getDeclaredMethod("equals", object);
            TO_STRING = object.getDeclaredMethod("toString");
        } catch (NoSuchMethodException e) {
            // Never happens.
            throw new Error(e);
        }
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
        // The clone() method is not handled as there's no way to emulate the behavior of that method.
        // Implementers would have to implement cloning entirely in a subclass.
        if (method.equals(HASH_CODE)) {
            return objectHashCode(proxy);
        }
        if (method.equals(EQUALS)) {
            return objectEquals(proxy, args[0]);
        }
        if (method.equals(TO_STRING)) {
            return objectToString(proxy);
        }
        throw new UnsupportedOperationException(method.getName());
    }
    
    public String objectClassName(Object obj) {
        return obj.getClass().getName();
    }
    
    public int objectHashCode(Object obj) {
        return System.identityHashCode(obj);
    }
    
    public boolean objectEquals(Object obj, Object other) {
        return obj == other;
    }
    
    public String objectToString(Object obj) {
        return objectClassName(obj) + '@' + Integer.toHexString(objectHashCode(obj));
    }
}

然后使用以下命令创建代理

Proxy.newProxyInstance(
    interfaceType.getClassLoader(),
    new Class<?>[]{interfaceType},
    new ObjectInvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
            if (method.equals(m)) {
                return f.apply(args);
            }
            return super.invoke(proxy, method, args);
        }
    }
)

相关问题