- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
假设我在给定类型(类/接口(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);
}
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");
}
}
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/40085048/
假设我有一个类,我在其中重载了运算符 == : Class A { ... public: bool operator== (const A &rhs) const; ... };
我知道你不应该使用 std::find(some_map.begin(), some_map.end()) 或 std::lower_bound,因为它会采用线性时间而不是 some_map.lowe
我正在尝试在 Haskell 中定义 Vector3 数据类型,并允许在其上使用 (+) 运算符。我尝试了以下方法: data Vector3 = Vector3 Double Double Doub
我已经为我的类图将运算符重载为“-”。它的用途并不完全直观(糟糕的编码 - 我知道)但是如果我做 graph3 = graph2-graph1 那么图 3 是应该只接收图 2 和图 1 中的那些顶点。
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Operator overloading 我想重载 以按字母顺序排列字符串,但我不确定该怎么做。 如何再次
下面的代码给我一个编译错误。谁能告诉我为什么? class mytype { public: int value; mytype(int a) { value = a;
这有什么问题吗? class Vec2 attr_accessor :x, :y # ... def += (v) @x += v.x @y += v.y retu
是否可以重载 [] 运算符两次?允许这样的事情:function[3][3](就像在二维数组中一样)。 如果可能的话,我想看看一些示例代码。 最佳答案 您可以重载 operator[] 以返回一个对象
我的团队目前正在与 Lua 合作,创建一个 android 游戏。我们遇到的一件事是表面上无法创建重载构造函数。 我习惯于使用默认值设置一个对象,然后在需要时使其过载。 前任: apples() {
我有一个网页,在某个时候显示一个导航栏,它只不过是一个 a 元素的列表 (ul)。所述 a 元素的大多数样式规则都是通用的。唯一应该改变的部分是要显示的图像,可以从列表中每个 li 元素的 id 标签
我对使用/重载“范围步长”运算符(.. ..)很感兴趣,但我终其一生都无法了解如何使用它。 在文档中它说 // Usage: start .. step .. finish 但是在 F# shell
Java 11(可能无关紧要): public static String toString(Object obj) { return ReflectionToStringBuilder.to
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我无法理解以下代码(针对行号进行注释) class Base { void m1(Object o) { } void m2(String o) { } } publi
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
class myclass{ //definitions here }; myclass e; int myarray[10]; /* Do something... */ e = myarray;
为什么不能将下标运算符(operator [])作为 friend 函数重载? 最佳答案 正如Bjarne Stroustrup在D&E book中所说: However, even in the o
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
因此,我有一个问题是我最近尝试重载 namespace Eng { /** * A structure to represent pixels */ typedef
如何重载onResume()以正确的方式工作?我想从 activity 返回到 MainActivity ,我希望在其中具有与应用程序启动后相同的状态。我想使用 recreate() 但它循环了或者类
我是一名优秀的程序员,十分优秀!