Panasonic Youth rob sanheim writes about software, business, ruby, music, stuff and things



Posted
8 December 2005 @ 1am

Tagged
Ajax, Java

Discuss

Using reflection to determine method overloading

A thread on the DWR mailing list about properly detected overloaded methods with covariant types in JDK5 got me curious. So I put together some quick code to determine if one method overloads another. It works for covariant types, but I didn't add support for generics. I'm not sure how much more work that would be.

Full source code follows. Feel free to use it if you like it, just leave the attribution. I'm sure there could be more testing and some optimization done, also. I would also be very curious to know if anyone else has had to do something similiar.

Here's the main util class that does the work:

JAVA:
  1. package com.robsanheim.sandbox.reflection.overloading;
  2.  
  3. import java.lang.reflect.Method;
  4. import java.util.Arrays;
  5.  
  6. /**
  7. * @author Rob Sanheim
  8. *
  9. * A utility to check if one method is overloading another. Note that this
  10. * utility does not take into account generics at all, but it should work
  11. * correctly for covariant return types.
  12. * @link <a *       href="http://java.sun.com/docs/books/jls/third_edition/html/classes.html#227768">the
  13. *       JLS</a> for information on overloading
  14. *
  15. * TODO change to allow parameters to isOverloaded be ordered any way, and make this
  16. *   class figure out which is the "higher level" method
  17. *
  18. * TODO change to support generics, particularily the nasty case where an
  19. *   overloaded or overridden method uses generics and the base method does not -
  20. *   see <a *         href="http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8.3">JLS
  21. *   again</a>
  22. */
  23. public class OverloadUtil {
  24.     /**
  25.      * Check two methods reflectively to see if one is overloading the other
  26.      * Note that the parameter ordering is important if one method is higher in
  27.      * the class hiearchy then the other. If this is the case, make sure the
  28.      * method higher up in the chain is the first parameter. Otherwise, the
  29.      * ordering does not matter.
  30.      *
  31.      * @param higher
  32.      *            method
  33.      * @param lower
  34.      *            method
  35.      * @return
  36.      */
  37.     public static boolean isOverloaded(Method higher, Method lower) {
  38.         if (namesAreEqual(higher, lower) && returnTypesAreEqualOrCovariant(higher, lower)
  39.                 && isNotInterfaceImplementation(higher, lower) && isNotOverridden(higher, lower)) {
  40.             return true;
  41.  
  42.         } else {
  43.             return false;
  44.         }
  45.     }
  46.  
  47.     private static boolean isNotOverridden(Method higher, Method lower) {
  48.         if (isOverridden(higher, lower)) {
  49.             return false;
  50.         } else {
  51.             return true;
  52.         }
  53.     }
  54.  
  55.     /**
  56.      * @param higher
  57.      * @param lower
  58.      * @return true if lower overrides higher
  59.      */
  60.     private static boolean isOverridden(Method higher, Method lower) {
  61.         return declaringClassIsAssignableFrom(higher, lower) && declaringClassIsNotAnInterface(higher)
  62.                 && parametersAreEqual(higher, lower);
  63.     }
  64.  
  65.     /**
  66.      * @param first
  67.      * @param second
  68.      * @return true if the first method's declaring class is assignable from the
  69.      *         second
  70.      */
  71.     private static boolean declaringClassIsAssignableFrom(Method first, Method second) {
  72.         return first.getDeclaringClass().isAssignableFrom(second.getDeclaringClass());
  73.  
  74.     }
  75.  
  76.     /**
  77.      * We have to make sure we don't mistake standard interface implementation
  78.      * (where first method is on an interface and the params are equal) for
  79.      * overloading.
  80.      *
  81.      * @param higher
  82.      * @param lower
  83.      * @return
  84.      */
  85.     private static boolean isNotInterfaceImplementation(Method higher, Method lower) {
  86.         return !(declaringClassIsAnInterface(higher) && parametersAreEqual(higher, lower));
  87.     }
  88.  
  89.     /**
  90.      * check deep equality on parameters of two methods
  91.      *
  92.      * @param first
  93.      * @param second
  94.      * @return
  95.      */
  96.     private static boolean parametersAreEqual(Method first, Method second) {
  97.         return Arrays.deepEquals(first.getParameterTypes(), second.getParameterTypes());
  98.     }
  99.  
  100.     /**
  101.      * @param higher
  102.      * @param lower
  103.      * @return true if return types are equal or covariants
  104.      */
  105.     private static boolean returnTypesAreEqualOrCovariant(Method higher, Method lower) {
  106.         return (declaringClassIsAssignableFrom(higher, lower) || higher.getReturnType().equals(lower.getReturnType()));
  107.     }
  108.  
  109.     /**
  110.      * @param first
  111.      * @param second
  112.      * @return true if the names of the two methods are equal
  113.      */
  114.     private static boolean namesAreEqual(Method first, Method second) {
  115.         return first.getName().equals(second.getName());
  116.     }
  117.  
  118.     private static boolean declaringClassIsAnInterface(Method method) {
  119.         return method.getDeclaringClass().isInterface();
  120.     }
  121.  
  122.     private static boolean declaringClassIsNotAnInterface(Method method) {
  123.         return !declaringClassIsAnInterface(method);
  124.     }
  125.  
  126. }

Test case:

JAVA:
  1. package com.robsanheim.sandbox.reflection.overloading;
  2.  
  3. import java.lang.reflect.Method;
  4.  
  5. import junit.framework.TestCase;
  6.  
  7. /**
  8. * @author Rob Sanheim
  9. *
  10. * Test OverloadUtil
  11. * Unless otherwise noted, we cannot assume that isOverloaded(x, y) == isOverloaded(y, x)
  12. */
  13. public class OverloadUtilTest extends TestCase {
  14.     private static final String METHOD = "method";
  15.     private static final String ANOTHER_METHOD = "anotherMethod";
  16.     private static final Class[] NO_PARAMETERS = null;
  17.     // fixtures
  18.     private Method returnsStringNoParameters = getMethod(ConcreteClass.class, METHOD, NO_PARAMETERS);
  19.     private Method returnsStringOneParameter = getMethod(ConcreteClass.class, METHOD, String.class);
  20.     private Method returnsObjectNoParametersDifferentName = getMethod(ConcreteClass.class, ANOTHER_METHOD,
  21.             NO_PARAMETERS);
  22.     private Method returnsStringNoParametersOnSubClass = getMethod(SubClass.class, METHOD, NO_PARAMETERS);
  23.     private Method returnsObjectThreeParametersOnSubClass = getMethod(SubClass.class, METHOD, String.class,
  24.             String.class, String.class);
  25.     private Method returnsObjectNoParametersOnInterfaceType = getMethod(Interface.class, METHOD, NO_PARAMETERS);
  26.  
  27.     /**
  28.      * This type of test should be reversible
  29.      *
  30.      * @throws Exception
  31.      */
  32.     public void testTwoSimpleOverloadedMethodsOnSameClass() throws Exception {
  33.         assertTrue(OverloadUtil.isOverloaded(returnsStringNoParameters, returnsStringOneParameter));
  34.         assertTrue(OverloadUtil.isOverloaded(returnsStringOneParameter, returnsStringNoParameters));
  35.     }
  36.  
  37.     public void testMoreSpecificMethodOnConcreteClassOverloadsInterfaceMethods() throws Exception {
  38.         assertTrue(OverloadUtil.isOverloaded(returnsObjectNoParametersOnInterfaceType, returnsStringOneParameter));
  39.     }
  40.  
  41.     public void testInterfaceImplementationIsNotOverloading() throws Exception {
  42.         assertFalse(OverloadUtil.isOverloaded(returnsObjectNoParametersOnInterfaceType, returnsStringNoParameters));
  43.     }
  44.  
  45.     /**
  46.      * This type of test should be reversible
  47.      *
  48.      * @throws Exception
  49.      */
  50.     public void testIsNotOverloadingForDifferentNamesOnSameClass() throws Exception {
  51.         assertFalse(OverloadUtil.isOverloaded(returnsStringNoParameters, returnsObjectNoParametersDifferentName));
  52.         assertFalse(OverloadUtil.isOverloaded(returnsStringOneParameter, returnsObjectNoParametersDifferentName));
  53.     }
  54.  
  55.     public void testIsNotOverloadingForDifferentNamesInterfaceAgainstConcrete() throws Exception {
  56.         assertFalse(OverloadUtil.isOverloaded(returnsObjectNoParametersOnInterfaceType,
  57.                 returnsObjectNoParametersDifferentName));
  58.     }
  59.  
  60.     public void testOverridingIsNotOverloading() throws Exception {
  61.         assertFalse(OverloadUtil.isOverloaded(returnsStringNoParameters, returnsStringNoParametersOnSubClass));
  62.     }
  63.  
  64.     public void testMethodOnSubclassOverloadsInterfaceMethod() throws Exception {
  65.         assertTrue(OverloadUtil.isOverloaded(returnsObjectNoParametersOnInterfaceType,
  66.                 returnsObjectThreeParametersOnSubClass));
  67.     }
  68.  
  69.     /**
  70.      * This type of test should be reversible
  71.      *
  72.      * @throws Exception
  73.      */
  74.     public void testMethodOnSubclassOverloadsMethodOnSameClass() throws Exception {
  75.         assertTrue(OverloadUtil.isOverloaded(returnsStringNoParametersOnSubClass,
  76.                 returnsObjectThreeParametersOnSubClass));
  77.         assertTrue(OverloadUtil.isOverloaded(returnsObjectThreeParametersOnSubClass,
  78.                 returnsStringNoParametersOnSubClass));
  79.     }
  80.  
  81.     public void testMethodOnSubclassOverloadsMethodOnSuperClass() throws Exception {
  82.         assertTrue(OverloadUtil.isOverloaded(returnsStringOneParameter, returnsObjectThreeParametersOnSubClass));
  83.     }
  84.  
  85.     public void testMethodOnSubclassDoesNotOverloadMethodOfDifferentNameOnSuperClass() throws Exception {
  86.         assertFalse(OverloadUtil.isOverloaded(returnsObjectNoParametersDifferentName,
  87.                 returnsObjectThreeParametersOnSubClass));
  88.     }
  89.  
  90.     /**
  91.      * Util method to get a method, ignoring checked exceptions
  92.      *
  93.      * @param clazz
  94.      * @param name
  95.      * @param parameterTypes
  96.      * @return method
  97.      */
  98.     private static Method getMethod(Class clazz, String name, Class... parameterTypes) {
  99.         Method method = null;
  100.         try {
  101.             method = clazz.getMethod(name, parameterTypes);
  102.         } catch (Exception e) {
  103.             e.printStackTrace();
  104.         }
  105.         return method;
  106.     }
  107.  
  108. }

And finally, test fixtures:

JAVA:
  1. package com.robsanheim.sandbox.reflection.overloading;
  2.  
  3. public class ConcreteClass implements Interface {
  4.    
  5.     /** (non-Javadoc)
  6.      * implements the method in the interface type with a covariant return type
  7.      * @see com.robsanheim.sandbox.reflection.overloading.Interface#method()
  8.      */
  9.     public String method() {
  10.         return null;
  11.     }
  12.    
  13.     /**
  14.      * Overloads method in this concrete class, does not overload method from interface
  15.      * @param one
  16.      * @return
  17.      */
  18.     public String method(String one) {
  19.         return null;
  20.     }
  21.    
  22.     public Object anotherMethod() {
  23.         return null;
  24.     }
  25. }

JAVA:
  1. package com.robsanheim.sandbox.reflection.overloading;
  2.  
  3. /**
  4. * @author Rob Sanheim
  5. *
  6. * Basic interface for testing
  7. */
  8. public interface Interface {
  9.     public Object method();
  10. }

JAVA:
  1. package com.robsanheim.sandbox.reflection.overloading;
  2.  
  3. public class SubClass extends ConcreteClass {
  4.     /**
  5.      * Override method from concrete class
  6.      */
  7.     public String method() {
  8.         return null;
  9.     }
  10.    
  11.     public Object method(String one, String two, String three) {
  12.         return null;
  13.     }
  14. }


1 Comment

Posted by
User
6 November 2008 @ 10pm

Sweet! This is exactly what I was about to sit down and write. Thanks!


Leave a Comment

Amazon wishlists suck Keep your sql and your java code separate