Pages

Tuesday, January 11, 2011

Java Reflection getDeclaredMethod() and getDeclaredField() Examples with Primitives

George W. Hart
I recently have had to use some classes which were developed some time ago with a lot of complex methods and a number of fields. The problem was that these methods and fields are private. If they were protected, then I could subclass to gain access to them.

The example I have included below sets a field in a super class, Calendar, of GregorianCalendar. The field is also protected so I need to set its accessibility to set the field,  modify it, and reset the accessibility.

In the second example, I have a private method of the GregorianCalendar which takes two int arguments. This becomes very tricky since the getDeclaredMethod(String name, Class<?>... parameterTypes) requires Class arguments. Your initial response might suggest that you can use a wrapper class for your primitives such as Integer.class. This will not work.

There are eight Type wrappers for your Class<?> primitives including void: Integer.TYPE, Long.TYPE, Character.TYPE, Short.TYPE, Double.TYPE, Float.TYPE, Boolean.TYPE, and Void.TYPE. These are key to using the getDeclaredMethod() method. In the example I use, Integer.TYPE.

When you have your Method object, you must call the invoke(Object obj, Object... args). We use the instance of the Object we want to modify, and pass in an array of Object arguments. In this case, I use the wrapper method for Integer to wrap my primitive arguments.



/*
 *  Copyright 2011 Blue Lotus Software, LLC.
 *  Copyright 2011 John Yeary <jyeary@bluelotussoftware.com>.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
/*
 * $Id: App.java 331 2011-01-11 13:54:06Z jyeary $
 */
package com.bluelotussoftware.example.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.GregorianCalendar;

public class App {

    public static void main(String[] args) throws NoSuchFieldException, IllegalArgumentException,
            IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Field Reflection 
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        GregorianCalendar calendar = (GregorianCalendar) Calendar.getInstance();
        System.out.println("Unmodified Calendar Time (ms): " + calendar.getTimeInMillis());
        System.out.println("Unmodified Calendar Time (Date): " + calendar.getTime());

        /*
         * The field we want to modify is in the super class.
         */
        Field field = calendar.getClass().getSuperclass().getDeclaredField("time");

        /*
         * Since the field is protected we need to make it accessible since
         * our class does not extend GregorianCalendar.
         */
        field.setAccessible(true);

        /*
         * The field we are setting is in the super class, but we are using
         * the sub-class instance to set the field.
         */
        field.setLong(calendar, 4 * 60 * 60 * 1000L + 55 * 60 * 1000L); //23:55:00

        /*
         * Turn off the accessibility after our modification.
         */
        field.setAccessible(false);

        System.out.println("Modified Calendar Time (ms): " + calendar.getTimeInMillis());
        System.out.println("Modified Calendar Time (Date): " + calendar.getTime());

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Method Reflection
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        String methodName = "monthLength";

        /*
         * If you are using primitives, you must use their Class.TYPE
         * such as Boolean.TYPE, Integer.TYPE, Double.TYPE, etc.
         */
        Class[] methodParameters = new Class[]{Integer.TYPE, Integer.TYPE};

        /*
         * Month is 0 based so February = 1. 2008 is a leap year.
         */
        Object[] params = new Object[]{new Integer(1), new Integer(2008)};

        Method method = calendar.getClass().getDeclaredMethod(methodName, methodParameters);

        /**
         * The Method is private so we must make it accessible to modify it.
         */
        method.setAccessible(true);
        Integer monthLength = (Integer) method.invoke(calendar, params);
        method.setAccessible(false); // Reset accessibility
        System.out.println("Month length (2/2008): " + monthLength);
    }
}

6 comments :