如何使用反射获取为其创建集合对象的类类型 [英] How to get the class type for which a collection object is created using reflection

查看:67
本文介绍了如何使用反射获取为其创建集合对象的类类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要你的帮助来解决这个问题,我一直在努力寻找答案,但没有找到,而且我完成作业的时间越来越少.

I need your help to solve this, I have been trying to get an answer but didn't find one and I have very less time left to complete the assignment.

以下代码:

 public class MainClass {
      @SuppressWarnings("unchecked")
      public static void main(String args[]) throws ClassNotFoundException {
           @SuppressWarnings("rawtypes")
           Class aClass = EmployeeDao.class;
           Method[] methods = aClass.getDeclaredMethods();

           @SuppressWarnings("rawtypes")
           Class returnType = null;
           for (Method method : methods) {
               if (!method.getReturnType().equals(Void.TYPE)) {
                  System.out.println(method.getName());

                  returnType = method.getReturnType();
                  if (returnType.isAssignableFrom(HashSet.class)) {
                      System.out.println("hash set");
                  }  
               }
           }
     }
 }

在上面的代码中,我获取了一个类的所有方法并检查它的返回类型是否为HashSet,在这种情况下我需要找出set对象包含的对象类型,例如下面的EmployeeDao方法班级:

in the above code I am getting all the methods of a class and checking if its return type is HashSet and in that case I need to find out the type of object contained by the set object, for example the below method of EmployeeDao class:

public Set<Employee> findAllEmployees() {
     //some code here
}

我想为上述方法获取 Employee 的类对象,我不知道该怎么做.上面的代码是静态的,我知道在上面的情况下,但这必须动态完成,上面只是一个演示代码,该程序实时将获取其方法必须作为参数访问的类.

I want to get the class object for Employee for the above method, I am not getting how to do it. The code above is static and I know in the above case, but this has to be done dynamically and above is only a demo code, at real time this program will get the class whose methods has to be accessed as an argument.

推荐答案

在您的情况下,您可以使用 getGenericReturnType:

In your case you can use getGenericReturnType:

Type t = method.getGenericReturnType();

如果你打印t,你会发现它是一个java.lang.reflect.ParameterizedType Set.

If you print t, you'll find that it's a java.lang.reflect.ParameterizedType Set<Employee>.

然而,您现在已经踏入了java.lang.reflect.Type 及其子类型.如果你想正确地发现 t 是其他类型的子类型还是超类型,比如 HashSet,你需要实现 此处描述的算法,至少部分.

However, you've now stepped off a cliff in to the wacky world of java.lang.reflect.Type and its subtypes. If you wanted to properly discover whether t is a subtype or supertype of some other type, like HashSet<E>, you would need to implement the algorithm described here, at least in part.

要简单地获取 Employee 类,您可以执行以下操作:

To simply get the Employee class, you could do something like this:

if (t instanceof ParameterizedType) {
    Type[] args = ((ParameterizedType) t).getActualTypeArguments();
    for (Type arg : args) {
        // This will print e.g. 'class com.example.Employee'
        System.out.println(arg);
    }
}

<小时>

不过,作为一般注意事项,如果您遇到这样的情况:


As a general note about this, though, if you have some situation like this:

class Foo<T> {
    List<T> getList() {return ...;}
}

然后你这样做:

Foo<String>  f = new Foo<String>();
Method getList = f.getClass().getDeclaredMethod("getList");
Type   tReturn = getList.getGenericReturnType();
// This prints 'List<T>', not 'List<String>':
System.out.println(tReturn);

泛型返回类型是 List,因为 getGenericReturnType() 返回声明中的类型.如果你想例如从 Foo 的类中获取 List,你需要做一些类似 new Foo() {} 以便将类型参数保存在类文件中,然后执行一些魔术,将类型变量 T 的实例替换为 T 的类型参数.这开始进入一些非常重要的反思.

The generic return type is List<T>, because getGenericReturnType() returns the type in the declaration. If you wanted to e.g. get List<String> from the class of a Foo<String>, you'd need to do something like new Foo<String>() {} so that the type argument is saved in a class file, then do some magic whereby you replace instances of the type variable T with the type argument to T. This starts to get in to some really heavy-duty reflection.

编辑了一个示例,说明如何测试参数化类型的简单可分配性.

Edited with an example of how to test simple assignability of a parameterized type.

这将处理诸如 SetHashSet 之类的事情,以及示例中描述的稍微复杂一些的情况,使用 Foo F实现 SetBar扩展 Foo.这不处理像 List> 这样的嵌套类型或带有通配符的类型.那些更复杂.

This will handle something like Set<Employee> and HashSet<Employee> as well as cases like the somewhat more complicated case depicted in the example, with Foo<F> implements Set<F> and Bar<A, B> extends Foo<B>. This doesn't handle nested types like List<List<T>> or types with wildcards. Those are more complicated.

基本上,您可以找到泛型超类型,例如Set,然后用正确对应的类型参数替换每个类型变量(对于 Set 只是 E).

Basically, you find the generic supertype e.g. Set<E>, then replace each type variable (just E for Set) with the type argument which correctly corresponds to it.

package mcve;

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

class TypeTest {
    class Employee {}
    abstract class Foo<F>    implements Set<F> {}
    abstract class Bar<A, B> extends    Foo<B> {}
    Set<Employee> getSet() { return Collections.emptySet(); }

    public static void main(String[] args) throws ReflectiveOperationException {
        Method m = TypeTest.class.getDeclaredMethod("getSet");
        Type   r = m.getGenericReturnType();
        if (r instanceof ParameterizedType) {
            boolean isAssignable;
            isAssignable =
            //  Testing i.e. Set<Employee> assignable from HashSet<Employee>
                isNaivelyAssignable((ParameterizedType) r,
                                    HashSet.class,
                                    Employee.class);
            System.out.println(isAssignable);
            isAssignable =
            //  Testing i.e. Set<Employee> assignable from Bar<String, Employee>
                isNaivelyAssignable((ParameterizedType) r,
                                    Bar.class,
                                    String.class,
                                    Employee.class);
            System.out.println(isAssignable);
        }
    }

    static boolean isNaivelyAssignable(ParameterizedType sType,
                                       Class<?>          tRawType,
                                       Class<?>...       tArgs) {
        Class<?> sRawType = (Class<?>) sType.getRawType();
        Type[]   sArgs    = sType.getActualTypeArguments();
        // Take the easy way out, if possible.
        if (!sRawType.isAssignableFrom(tRawType)) {
            return false;
        }
        // Take the easy way out, if possible.
        if (sRawType.equals(tRawType)) {
            return Arrays.equals(sArgs, tArgs);
        }

        Deque<ParameterizedType> tHierarchyToS = new ArrayDeque<>();
        // Find the generic superclass of T whose raw type is the
        // same as S. For example, suppose we have the following
        // hierarchy and method:
        //  abstract class Foo<F>    implements Set<F> {}
        //  abstract class Bar<A, B> extends    Foo<B> {}
        //  class TypeTest { Set<Employee> getSet() {...} }
        // The we invoke isNaivelyAssignable as follows:
        //  Method m = TypeTest.class.getDeclaredMethod("getSet");
        //  Type   r = m.getGenericReturnType();
        //  if (t instanceof ParameterizedType) {
        //      boolean isAssignable =
        //          isNaivelyAssignable((ParameterizedType) r,
        //                              Bar.class,
        //                              String.class,
        //                              Employee.class);
        //  }
        // Clearly the method ought to return true because a
        // Bar<String, Employee> is a Set<Employee>.
        // To get there, first find the superclass of T
        // (T is Bar<String, Employee>) whose raw type is the
        // same as the raw type of S (S is Set<Employee>).
        // So we want to find Set<F> from the implements clause
        // in Foo.
        Type tParameterizedS = findGenericSuper(sRawType, tRawType, tHierarchyToS);
        if (tParameterizedS == null) {
            // Somebody inherited from a raw type or something.
            return false;
        }
        // Once we have Set<F>, we want to get the actual type
        // arguments to Set<F>, which is just F in this case.
        Type[] tArgsToSuper = tHierarchyToS.pop().getActualTypeArguments();
        if (tArgsToSuper.length != sArgs.length) {
            return false; // or throw IllegalArgumentException
        }
        // Then for each type argument to e.g. Set in the generic
        // superclass of T, we want to map that type argument to
        // one of tArgs. In the previous example, Set<F> should map
        // to Set<Employee> because Employee.class is what we passed
        // as the virtual type argument B in Bar<A, B> and B is what
        // is eventually provided as a type argument to Set.
        for (int i = 0; i < tArgsToSuper.length; ++i) {
            // tArgToSuper_i is the type variable F
            Type tArgToSuper_i = tArgsToSuper[i];

            if (tArgToSuper_i instanceof TypeVariable<?>) {
                // Copy the stack.
                Deque<ParameterizedType> tSupers = new ArrayDeque<>(tHierarchyToS);
                do {
                    TypeVariable<?> tVar_i = (TypeVariable<?>) tArgToSuper_i;
                    // The type variable F was declared on Foo so vDecl is
                    // Foo.class.
                    GenericDeclaration vDecl = tVar_i.getGenericDeclaration();
                    // Find the index of the type variable on its declaration,
                    // because we will use that index to look at the actual
                    // type arguments provided in the hierarchy. For example,
                    // the type argument F in Set<F> is at index 0 in Foo<F>.
                    // The type argument B to Foo<B> is at index 1 in Bar<A, B>.
                    TypeVariable<?>[] declVars = vDecl.getTypeParameters();
                    int tVarIndex = Arrays.asList(declVars).indexOf(tVar_i);
                    // Eventually we will walk backwards until we actually hit
                    // the class we passed in to the method, Bar.class, and are
                    // able to map the type variable on to one of the type
                    // arguments we passed in.
                    if (vDecl.equals(tRawType)) {
                        tArgToSuper_i = tArgs[tVarIndex];
                    } else {
                        // Otherwise we have to start backtracking through
                        // the stack until we hit the class where this type
                        // variable is declared. (It should just be the first
                        // pop(), but it could be the type variable is declared
                        // on e.g. a method or something, in which case we
                        // will empty the stack looking for it and eventually
                        // break from the loop and return false.)
                        while (!tSupers.isEmpty()) {
                            ParameterizedType tSuper    = tSupers.pop();
                            Class<?>          tRawSuper = (Class<?>) tSuper.getRawType();
                            if (vDecl.equals(tRawSuper)) {
                                tArgToSuper_i = tSuper.getActualTypeArguments()[tVarIndex];
                                break;
                            }
                        }
                    }
                } while (tArgToSuper_i instanceof TypeVariable<?>);
            }

            if (!tArgToSuper_i.equals(sArgs[i])) {
                return false;
            }
        }

        return true;
    }

    // If we have a raw type S which is Set from e.g. the parameterized
    // type Set<Employee> and a raw type T which is HashSet from e.g.
    // the parameterized type HashSet<Employee> we want to find the
    // generic superclass of HashSet which is the same as S, pushing
    // each class in between on to the stack for later. Basically
    // just walk upwards pushing each superclass until we hit Set.
    // For e.g. s = Set.class and t = HashSet.class, then:
    //  tHierarchyToS = [Set<E>, AbstractSet<E>].
    // For e.g. s = Set.class and t = Bar.class, then:
    //  tHierarchyToS = [Set<F>, Foo<B>]
    static ParameterizedType findGenericSuper(Class<?> s,
                                              Class<?> t,
                                              Deque<ParameterizedType> tHierarchyToS) {
        ParameterizedType tGenericSuper = null;

        do {
            List<Type> directSupertypes = new ArrayList<>();
            directSupertypes.add(t.getGenericSuperclass());
            Collections.addAll(directSupertypes, t.getGenericInterfaces());

            for (Type directSuper : directSupertypes) {
                if (directSuper instanceof ParameterizedType) {
                    ParameterizedType pDirectSuper = (ParameterizedType) directSuper;
                    Class<?>          pRawSuper    = (Class<?>) pDirectSuper.getRawType();

                    if (s.isAssignableFrom(pRawSuper)) {
                        tGenericSuper = pDirectSuper;
                        t             = pRawSuper;
                        tHierarchyToS.push(tGenericSuper);
                        break;
                    }
                }
            }
        } while (!s.equals(t) && tGenericSuper != null);

        return tGenericSuper;
    }
}

这篇关于如何使用反射获取为其创建集合对象的类类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆