Java Reflection Method Invoke invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)

Here you can find the source of invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)

Description

Invoke a named method whose parameter type matches the object type.

License

Apache License

Parameter

Parameter Description
object invoke method on this object
methodName get method with this name
args use these arguments - treat null as empty array
parameterTypes match these parameters - treat null as empty array

Exception

Parameter Description
NoSuchMethodException if there is no such accessible method
InvocationTargetException wraps an exception thrown by the method invoked
IllegalAccessException if the requested method is not accessible via reflection

Declaration

public static Object invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)
        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException 

Method Source Code

//package com.java2s;
/*//from  w  w  w  .ja v a2  s.  co  m
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Main {
    /** An empty class array */
    private static final Class[] emptyClassArray = new Class[0];
    /** An empty object array */
    private static final Object[] emptyObjectArray = new Object[0];

    /**
     * <p>
     * Invoke a named method whose parameter type matches the object type.
     * </p>
     * 
     * <p>
     * The behaviour of this method is less deterministic than
     * {@link #invokeExactMethod}. It loops through all methods with names that
     * match and then executes the first it finds with compatable parameters.
     * </p>
     * 
     * <p>
     * This method supports calls to methods taking primitive parameters via
     * passing in wrapping classes. So, for example, a <code>Boolean</code>
     * class would match a <code>boolean</code> primitive.
     * </p>
     * 
     * <p>
     * This is a convenient wrapper for
     * {@link #invokeMethod(Object object,String methodName,Object [] args)}.
     * </p>
     * 
     * @param object
     *            invoke method on this object
     * @param methodName
     *            get method with this name
     * @param arg
     *            use this argument
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeMethod(Object object, String methodName, Object arg)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

        Object[] args = { arg };
        return invokeMethod(object, methodName, args);

    }

    /**
     * <p>
     * Invoke a named method whose parameter type matches the object type.
     * </p>
     * 
     * <p>
     * The behaviour of this method is less deterministic than
     * {@link #invokeExactMethod(Object object,String methodName,Object [] args)}
     * . It loops through all methods with names that match and then executes
     * the first it finds with compatable parameters.
     * </p>
     * 
     * <p>
     * This method supports calls to methods taking primitive parameters via
     * passing in wrapping classes. So, for example, a <code>Boolean</code>
     * class would match a <code>boolean</code> primitive.
     * </p>
     * 
     * <p>
     * This is a convenient wrapper for
     * {@link #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}
     * .
     * </p>
     * 
     * @param object
     *            invoke method on this object
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeMethod(Object object, String methodName, Object[] args)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

        if (args == null) {
            args = emptyObjectArray;
        }
        int arguments = args.length;
        Class parameterTypes[] = new Class[arguments];
        for (int i = 0; i < arguments; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        return invokeMethod(object, methodName, args, parameterTypes);

    }

    /**
     * <p>
     * Invoke a named method whose parameter type matches the object type.
     * </p>
     * 
     * <p>
     * The behaviour of this method is less deterministic than
     * {@link #invokeExactMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}
     * . It loops through all methods with names that match and then executes
     * the first it finds with compatable parameters.
     * </p>
     * 
     * <p>
     * This method supports calls to methods taking primitive parameters via
     * passing in wrapping classes. So, for example, a <code>Boolean</code>
     * class would match a <code>boolean</code> primitive.
     * </p>
     * 
     * 
     * @param object
     *            invoke method on this object
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * @param parameterTypes
     *            match these parameters - treat null as empty array
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

        if (parameterTypes == null) {
            parameterTypes = emptyClassArray;
        }
        if (args == null) {
            args = emptyObjectArray;
        }

        Method method = getMatchingAccessibleMethod(object.getClass(), methodName, parameterTypes);
        if (method == null)
            throw new NoSuchMethodException(
                    "No such accessible method: " + methodName + "() on object: " + object.getClass().getName());
        return method.invoke(object, args);
    }

    /**
     * <p>
     * Find an accessible method that matches the given name and has compatible
     * parameters. Compatible parameters mean that every method parameter is
     * assignable from the given parameters. In other words, it finds a method
     * with the given name that will take the parameters given.
     * <p>
     * 
     * <p>
     * This method is slightly undeterminstic since it loops through methods
     * names and return the first matching method.
     * </p>
     * 
     * <p>
     * This method is used by
     * {@link #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
     * 
     * <p>
     * This method can match primitive parameter by passing in wrapper classes.
     * For example, a <code>Boolean</code> will match a primitive
     * <code>boolean</code> parameter.
     * 
     * @param clazz
     *            find method in this class
     * @param methodName
     *            find method with this name
     * @param parameterTypes
     *            find method with compatible parameters
     */
    public static Method getMatchingAccessibleMethod(Class clazz, String methodName, Class[] parameterTypes) {

        // see if we can find the method directly
        // most of the time this works and it's much faster
        try {
            Method method = clazz.getMethod(methodName, parameterTypes);

            try {
                //
                // XXX Default access superclass workaround
                //
                // When a public class has a default access superclass
                // with public methods, these methods are accessible.
                // Calling them from compiled code works fine.
                //
                // Unfortunately, using reflection to invoke these methods
                // seems to (wrongly) to prevent access even when the method
                // modifier is public.
                //
                // The following workaround solves the problem but will only
                // work from sufficiently privilages code.
                //
                // Better workarounds would be greatfully accepted.
                //
                method.setAccessible(true);

            } catch (SecurityException se) {
            }
            return method;

        } catch (NoSuchMethodException e) { /* SWALLOW */
        }

        // search through all methods
        int paramSize = parameterTypes.length;
        Method[] methods = clazz.getMethods();
        for (int i = 0, size = methods.length; i < size; i++) {
            if (methods[i].getName().equals(methodName)) {
                // compare parameters
                Class[] methodsParams = methods[i].getParameterTypes();
                int methodParamSize = methodsParams.length;
                if (methodParamSize == paramSize) {
                    boolean match = true;
                    for (int n = 0; n < methodParamSize; n++) {
                        if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
                            match = false;
                            break;
                        }
                    }

                    if (match) {
                        // get accessible version of method
                        Method method = getAccessibleMethod(methods[i]);
                        if (method != null) {
                            try {
                                //
                                // XXX Default access superclass workaround
                                // (See above for more details.)
                                //
                                method.setAccessible(true);

                            } catch (SecurityException se) {
                            }
                            return method;
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * <p>
     * Determine whether a type can be used as a parameter in a method
     * invocation. This method handles primitive conversions correctly.
     * </p>
     * 
     * <p>
     * In order words, it will match a <code>Boolean</code> to a
     * <code>boolean</code>, a <code>Long</code> to a <code>long</code>, a
     * <code>Float</code> to a <code>float</code>, a <code>Integer</code> to a
     * <code>int</code>, and a <code>Double</code> to a <code>double</code>. Now
     * logic widening matches are allowed. For example, a <code>Long</code> will
     * not match a <code>int</code>.
     * 
     * @param parameterType
     *            the type of parameter accepted by the method
     * @param parameterization
     *            the type of parameter being tested
     * 
     * @return true if the assignement is compatible.
     */
    protected static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) {
        // try plain assignment
        if (parameterType.isAssignableFrom(parameterization)) {
            return true;
        }

        if (parameterType.isPrimitive()) {
            // does anyone know a better strategy than comparing names?
            // also, this method does *not* do widening - you must specify
            // exactly
            // is this the right behaviour?
            if (boolean.class.equals(parameterType)) {
                return Boolean.class.equals(parameterization);
            }
            if (float.class.equals(parameterType)) {
                return Float.class.equals(parameterization);
            }
            if (long.class.equals(parameterType)) {
                return Long.class.equals(parameterization);
            }
            if (int.class.equals(parameterType)) {
                return Integer.class.equals(parameterization);
            }
            if (double.class.equals(parameterType)) {
                return Double.class.equals(parameterization);
            }
        }

        return false;
    }

    /**
     * <p>
     * Return an accessible method (that is, one that can be invoked via
     * reflection) with given name and a single parameter. If no such method can
     * be found, return <code>null</code>. Basically, a convenience wrapper that
     * constructs a <code>Class</code> array for you.
     * </p>
     * 
     * @param clazz
     *            get method from this class
     * @param methodName
     *            get method with this name
     * @param parameterType
     *            taking this type of parameter
     */
    public static Method getAccessibleMethod(Class clazz, String methodName, Class parameterType) {

        Class[] parameterTypes = { parameterType };
        return getAccessibleMethod(clazz, methodName, parameterTypes);

    }

    /**
     * <p>
     * Return an accessible method (that is, one that can be invoked via
     * reflection) with given name and parameters. If no such method can be
     * found, return <code>null</code>. This is just a convenient wrapper for
     * {@link #getAccessibleMethod(Method method)}.
     * </p>
     * 
     * @param clazz
     *            get method from this class
     * @param methodName
     *            get method with this name
     * @param parameterTypes
     *            with these parameters types
     */
    public static Method getAccessibleMethod(Class clazz, String methodName, Class[] parameterTypes) {

        try {
            return getAccessibleMethod(clazz.getMethod(methodName, parameterTypes));
        } catch (NoSuchMethodException e) {
            return (null);
        }

    }

    /**
     * <p>
     * Return an accessible method (that is, one that can be invoked via
     * reflection) that implements the specified Method. If no such method can
     * be found, return <code>null</code>.
     * </p>
     * 
     * @param method
     *            The method that we wish to call
     */
    public static Method getAccessibleMethod(Method method) {

        // Make sure we have a method to check
        if (method == null) {
            return (null);
        }

        // If the requested method is not public we cannot call it
        if (!Modifier.isPublic(method.getModifiers())) {
            return (null);
        }

        // If the declaring class is public, we are done
        Class clazz = method.getDeclaringClass();
        if (Modifier.isPublic(clazz.getModifiers())) {
            return (method);
        }

        // Check the implemented interfaces and subinterfaces
        method = getAccessibleMethodFromInterfaceNest(clazz, method.getName(), method.getParameterTypes());
        return (method);

    }

    /**
     * <p>
     * Return an accessible method (that is, one that can be invoked via
     * reflection) that implements the specified method, by scanning through all
     * implemented interfaces and subinterfaces. If no such method can be found,
     * return <code>null</code>.
     * </p>
     * 
     * <p>
     * There isn't any good reason why this method must be private. It is
     * because there doesn't seem any reason why other classes should call this
     * rather than the higher level methods.
     * </p>
     * 
     * @param clazz
     *            Parent class for the interfaces to be checked
     * @param methodName
     *            Method name of the method we wish to call
     * @param parameterTypes
     *            The parameter type signatures
     */
    private static Method getAccessibleMethodFromInterfaceNest(Class clazz, String methodName,
            Class parameterTypes[]) {

        Method method = null;

        // Search up the superclass chain
        for (; clazz != null; clazz = clazz.getSuperclass()) {

            // Check the implemented interfaces of the parent class
            Class interfaces[] = clazz.getInterfaces();
            for (int i = 0; i < interfaces.length; i++) {

                // Is this interface public?
                if (!Modifier.isPublic(interfaces[i].getModifiers()))
                    continue;

                // Does the method exist on this interface?
                try {
                    method = interfaces[i].getDeclaredMethod(methodName, parameterTypes);
                } catch (NoSuchMethodException e) {
                    ;
                }
                if (method != null)
                    break;

                // Recursively check our parent interfaces
                method = getAccessibleMethodFromInterfaceNest(interfaces[i], methodName, parameterTypes);
                if (method != null)
                    break;

            }

        }

        // If we found a method return it
        if (method != null)
            return (method);

        // We did not find anything
        return (null);

    }
}

Related

  1. invokeMethod(Object object, String methodName, Class[] parameterTypes, Object[] parameters)
  2. invokeMethod(Object object, String methodName, Class[] signature, Object[] parameters)
  3. invokeMethod(Object object, String methodName, List argList)
  4. invokeMethod(Object object, String methodName, Object arg, Class argType)
  5. invokeMethod(Object object, String methodName, Object... parameters)
  6. invokeMethod(Object object, String methodName, Object[] params, Object[] result)
  7. invokeMethod(Object object, String name, Object... arguments)
  8. invokeMethod(Object object, String propertyName)
  9. invokeMethod(Object objectInstance, String methodToInvoke, Class[] parameterTypes, Object[] instanceParameters)