Convert ListT to T ... dynamically in calling a method

4

I'm in need of something that seems to be simple, but I've been researching and trying for some time and have not found anything that helps.

Problem

I have a list of type List<T> that I get as a parameter of another method, and I have to make a call to a method that receives T... as a parameter.

Question

How would I dynamically convert a List<T> to T... ?

Example

For example, I'll show you what I'm doing in my real situation:

private Object invokeMethodPrivateClassBase(Class<?> clazz,
        String nameMethod, List<Class<?>> paramsTypeClazz, Object... args) {
    try {
        // esse método 'getDeclaredMethod' recebe o primeiro parâmetro String e o segundo Object..., 
        // mas eu não posso receber 2 (dois) parametros '?...', pois só o ultimo pode ser desse tipo (por questões obvias).
        Method method = clazz.getDeclaredMethod(nameMethod, paramsTypeClazz);
        method.setAccessible(true);
        return method.invoke(AbsHorizontalListView.this, args);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    return null;
}
    
asked by anonymous 31.07.2014 / 22:08

2 answers

4

I have prepared a functional example of how the method with miscellaneous arguments should be called.

The main idea is you should pass an array of parameters to invoke , but the arguments of miscellaneous size must be another array that will be the last element of the main array.

Consider the comments in the code that explain step-by-step what it does:

class Exemplo {

    private static Object invokeMethodPrivateClassBase(
            Class<?> clazz,
            Object instance,
            String nameMethod, 
            List<Class<?>> paramsTypeClazz, 
            Object... args) {
        try {

            //localiza o método
            Method method = clazz.getDeclaredMethod(
                nameMethod, 
                paramsTypeClazz.toArray(new Class[paramsTypeClazz.size()]));

            //torna-o acessível mesmo sendo private
            method.setAccessible(true);

            //verifica se tem argumentos variados
            if (method.isVarArgs()) {

                //cria um array de parâmetros que deve ter a exata quantidade de parâmetros declarados,
                //isto é, sem considerar os argumentos variantes
                Object[] parameters = new Object[paramsTypeClazz.size()];

                //copia os parâmetros fixos para o novo array, desconsiderando apenas o último que é variante
                System.arraycopy(args, 0, parameters, 0, paramsTypeClazz.size() - 1);

                //calcula quantos argumentos variantes foram passados
                int varArgsSize = args.length - paramsTypeClazz.size() + 1;

                //cria um array com o tipo dos parâmetros variantes
                Object varArgsParameter = Array.newInstance(
                    paramsTypeClazz.get(paramsTypeClazz.size() - 1).getComponentType(), 
                    varArgsSize);

                //copia todos os demais argumentos para o array de argumentos variantes
                System.arraycopy(args, paramsTypeClazz.size() - 1, varArgsParameter, 0, varArgsSize);

                //coloca o array de argumentos variantes como último parâmetro para o método a ser chamado
                //isso porque o "..." equivale a um array
                parameters[paramsTypeClazz.size() - 1] = varArgsParameter;

                //chama o método
                return method.invoke(instance, parameters);
            } else {
                return method.invoke(instance, args);
            }
        } catch (Throwable e) {
            //apenas para teste
            System.out.println(e.getMessage());
        }
        return "Y";
    }

    /**
     * Método de exemplo a ser chamado via reflexão 
     */
    public String meuMetodo(String label, Integer... valores) {
        Integer soma = 0;
        for (Integer v : valores) soma += v;
        return label + ": " + soma;
    }

    public static void main (String[] args) throws java.lang.Exception {

        //modo normal
        System.out.println(new Exemplo().meuMetodo("S1", 3, 4, 1));

        //prepara lista de tipos dos argumentos
        List<Class<?>> paramsTypeClazz = new ArrayList<Class<?>>();
        paramsTypeClazz.add(String.class);
        paramsTypeClazz.add(Integer[].class);

        //modo reflexivo
        System.out.println(Exemplo.invokeMethodPrivateClassBase(
            Exemplo.class,
            new Exemplo(),
            "meuMetodo",
            paramsTypeClazz,
            "S1", 3, 4, 1));
    }
}

Demo on Ideone

    
31.07.2014 / 23:52
3

This method of the Class class in the Java API receives a string and a varargs of Class<?> . The real problem then is to convert your List<Class<?>> paramsTypeClazz to varargs or an array. In this case try the following (not tested):

Class<?>[] arrayDeClass = paramsTypeClass.toArray(new Class<?>[paramsTypeClass.size()]);
Method method = clazz.getDeclaredMethod(nameMethod, arrayDeClass);

Any question is just to return. Hugs

    
31.07.2014 / 22:51