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

Hal*_*ast 7 java reflection

我正在编写一个应用程序,需要在运行时实现功能接口。我事先不知道要实现哪些接口,但可以Method使用接口Class对象的反射来解析对象。

为了简化问题,假设我传递一个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());
            }
    );
}
Run Code Online (Sandbox Code Playgroud)

因此,对toStringhashCode、 和 的调用equals当前失败。我显然不希望这样。

来自以下文档java.lang.reflect.Proxy

代理实例上声明的hashCodeequalstoString方法的调用java.lang.Object将被编码并分派到调用处理程序的调用方法,其方式与接口方法调用的编码和分派相同,如上所述。传递给调用的对象的声明类Method将为java.lang.Object。继承自的代理实例的其他公共方法java.lang.Object不会被代理类覆盖,因此这些方法的调用行为就像对java.lang.Object.

所以我可以根据需要重写这些方法。这很酷,但我宁愿不这样做。

当然,我可以制作虚拟实现,或多或少执行相同的操作(除了hashCodeis native)或捕获其他虚拟对象(可能是调用处理程序本身)并调用该对象上的方法。但我觉得我应该能够“放弃”从java.lang.Object通常方式继承的方法。或者换句话说,super.hashCode()直接在 上调用等价的内容proxy

有没有好的/标准的方法来做到这一点?

Hal*_*ast 4

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

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用创建代理

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);
        }
    }
)
Run Code Online (Sandbox Code Playgroud)