反射检查对象是否是方法的有效通用参数

如何使用反射来检查给定对象是否是方法的有效参数(参数和对象是泛型类型)?

为了获得一些背景知识,我正在努力实现这一目标:

在使用反射方法调用时,我认为可以调用具有特定类型参数的所有方法。 这对于原始类型非常适用,因为您可以在其类对象上调用isAssignableFrom(Class<?> c) 。 然而,当你开始在混合中投入泛型时,它突然间并不那么容易,因为泛型不是反射的原始设计的一部分,也因为类型删除。

问题较大,但基本归结为以下几点:

理想的解决方

理想的代码

import java.lang.reflect.*;
import java.util.*;


public class ReflectionAbuse {

    public static void callMeMaybe(List<Integer> number) {
        System.out.println("You called me!");
    }

    public static void callMeAgain(List<? extends Number> number) {
        System.out.println("You called me again!");
    }

    public static void callMeNot(List<Double> number) {
        System.out.println("What's wrong with you?");
    }

    public static <T> void reflectiveCall(List<T> number){
        for(Method method : ReflectionAbuse.class.getDeclaredMethods()) {
            if(method.getName().startsWith("call")) {
                if(canBeParameterOf(method, number)) {
                    try {
                        method.invoke(null, number);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static <T> boolean canBeParameterOf(Method method, List<T> number) {
        // FIXME some checks missing
        return true;
    }

    public static void main(String[] args) {
        reflectiveCall(new ArrayList<Integer>());
    }

}

会打印

You called me!
You called me again!

这应该是可能的,不管T如何看起来像(即它可能是另一个泛型类型,例如List<List<Integer>> )。

显然这不能工作,因为类型T在运行时被擦除并且未知。

尝试1

我能做的第一件事就是这样的:

import java.lang.reflect.*;
import java.util.*;


public class ReflectionAbuse {

            public static void callMeMaybe(ArrayList<Integer> number) {
                System.out.println("You called me!");
            }

            public static void callMeAgain(ArrayList<? extends Number> number) {
                System.out.println("You called me again!");
            }

            public static void callMeNot(ArrayList<Double> number) {
                System.out.println("What's wrong with you?");
            }

    public static <T> void reflectiveCall(List<T> number){
        for(Method method : ReflectionAbuse.class.getDeclaredMethods()) {
            if(method.getName().startsWith("call")) {
                if(canBeParameterOf(method, number)) {
                    try {
                        method.invoke(null, number);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static <T> boolean canBeParameterOf(Method method, List<T> number) {
        return method.getGenericParameterTypes()[0].equals(number.getClass().getGenericSuperclass());
    }

    public static void main(String[] args) {
        reflectiveCall(new ArrayList<Integer>(){});
    }

}

只打印

You called me!

但是,这有一些额外的警告:

  • 它仅适用于直接实例,因为Type接口不提供所需的方法,所以不会考虑继承层次结构。 在这里和那里演员肯定可以帮助你找到答案(另见我的第二次尝试)
  • 的参数reflectiveCall实际上需要为想要的参数类型的子类(注意{}new ArrayList<Integer>(){}其中创建匿名内部类)。 这显然不太理想:创建不必要的类对象,并且容易出错。 这是我可以考虑绕过类型擦除的唯一方法。
  • 尝试2

    考虑到由于删除而在理想解决方案中丢失的类型,人们也可以通过这种类型作为一个非常接近理想的理由:

    import java.lang.reflect.*;
    import java.util.*;
    
    
    public class ReflectionAbuse {
    
        public static void callMeMaybe(List<Integer> number) {
            System.out.println("You called me!");
        }
    
        public static void callMeAgain(List<? extends Number> number) {
            System.out.println("You called me again!");
        }
    
        public static void callMeNot(List<Double> number) {
            System.out.println("What's wrong with you?");
        }
    
        public static <T> void reflectiveCall(List<T> number, Class<T> clazz){
            for(Method method : ReflectionAbuse.class.getDeclaredMethods()) {
                if(method.getName().startsWith("call")) {
                    Type n = number.getClass().getGenericSuperclass();
                    if(canBeParameterOf(method, clazz)) {
                        try {
                            method.invoke(null, number);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        public static <T> boolean canBeParameterOf(Method method, Class<T> clazz) {
            Type type = ((ParameterizedType)method.getGenericParameterTypes()[0]).getActualTypeArguments()[0];
            if (type instanceof WildcardType) {
                return ((Class<?>)(((WildcardType) type).getUpperBounds()[0])).isAssignableFrom(clazz);
            }
            return ((Class<?>)type).isAssignableFrom(clazz);
        }
    
        public static void main(String[] args) {
            reflectiveCall(new ArrayList<Integer>(), Integer.class);
        }
    
    }
    

    这实际上打印正确的解决方案。 但这也不是没有消极的一面:

  • reflectiveCall的用户需要传递不必要和乏味的类型参数。 至少在编译时检查正确的调用。
  • 类型参数之间的继承没有被完全考虑,并且在canBeParameterOf还有很多需要实现的canBeParameterOf (例如类型化参数)。
  • 最大的问题是:类型参数本身不能通用,所以Integers List List不能用作参数。
  • 这个问题

    有什么我可以做的不同,以尽可能接近我的目标? 我坚持使用匿名子类或传递类型参数? 目前,我会解决给出的参数,因为这可以让你编译时间安全。

    在递归检查参数类型时是否需要注意什么?

    有没有可能让泛型成为解决方案2中的类型参数?

    其实,为了学习的目的,我想推出我自己的解决方案,而不是使用库,尽管我不介意看看一些内部工作原理。


    为了清楚起见,我例如知道以下内容,但试着保持例子的清洁:

  • 这可以在不反思的情况下解决(重新设计一些需求并使用例如接口和内部类)。 这是为了学习的目的。 我想解决的问题实际上有点大,但这就是它归结的原因。
  • 我可以使用注释来代替使用命名模式。 我其实是这样做的,但我想尽可能保持这些示例的独立性。
  • getGenericParameterTypes()返回的数组可能是空的,但我们假设所有方法都有一个参数,并且事先检查它。
  • 可能有非静态方法在调用null时失败。 假设没有。
  • catch条件可能更具体。
  • 有些演员需要更安全。

  • 我坚持使用匿名子类或传递类型参数?

    或多或少,但更好使用超类型标记模式而不是继承你的值类型。 毕竟,你的值类型可能不允许子类。 使用类型标记模式允许您接受泛型类型,而将Class接受为类型参数只允许原始类型(这就是为什么您必须采用List类型的组件,而不是类型本身)。

    public static <T> void reflectiveCall(TypeToken<T> type, T value)
    

    Guava对创建和使用TypeToken有很大的支持。 到目前为止,创建一个最简单的方法是创建一个匿名的子类(注意:如果它将被重用,使其成为一个常量):

    reflectiveCall(new TypeToken<List<Integer>>() {}, new ArrayList<Integer>());
    

    一旦你有了这个, canBeParameterOf变得更容易实现。

    public static boolean canBeParameterOf(Method method, TypeToken<?> givenType) {
        Type[] argTypes = method.getGenericParameterTypes();
    
        return argTypes.length != 0 && 
            TypeToken.of(argTypes[0]).isAssignableFrom(givenType);
    }
    
    链接地址: http://www.djcxy.com/p/13347.html

    上一篇: Reflectively checking whether a object is a valid generic argument to a method

    下一篇: Rapheal Pie Chart Rendering Issue in IE 9