import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author Rob Sanheim
*
* A utility to check if one method is overloading another. Note that this
* utility does not take into account generics at all, but it should work
* correctly for covariant return types.
* @link <a * href="http://java.sun.com/docs/books/jls/third_edition/html/classes.html#227768">the
* JLS</a> for information on overloading
*
* TODO change to allow parameters to isOverloaded be ordered any way, and make this
* class figure out which is the "higher level" method
*
* TODO change to support generics, particularily the nasty case where an
* overloaded or overridden method uses generics and the base method does not -
* see <a * href="http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8.3">JLS
* again</a>
*/
public class OverloadUtil
{
/**
* Check two methods reflectively to see if one is overloading the other
* Note that the parameter ordering is important if one method is higher in
* the class hiearchy then the other. If this is the case, make sure the
* method higher up in the chain is the first parameter. Otherwise, the
* ordering does not matter.
*
* @param higher
* method
* @param lower
* method
* @return
*/
public static boolean isOverloaded(Method higher, Method lower)
{
if (namesAreEqual(higher, lower) && returnTypesAreEqualOrCovariant(higher, lower) && isNotInterfaceImplementation(higher, lower)
&& isNotOverridden(higher, lower))
{
return true;
}
else
{
return false;
}
}
private static boolean isNotOverridden(Method higher, Method lower)
{
if (isOverridden(higher, lower))
{
return false;
}
else
{
return true;
}
}
/**
* @param higher
* @param lower
* @return true if lower overrides higher
*/
private static boolean isOverridden(Method higher, Method lower)
{
return declaringClassIsAssignableFrom(higher, lower) && declaringClassIsNotAnInterface(higher) && parametersAreEqual(higher, lower);
}
/**
* @param first
* @param second
* @return true if the first method's declaring class is assignable from the
* second
*/
private static boolean declaringClassIsAssignableFrom(Method first, Method second)
{
return first.getDeclaringClass().isAssignableFrom(second.getDeclaringClass());
}
/**
* We have to make sure we don't mistake standard interface implementation
* (where first method is on an interface and the params are equal) for
* overloading.
*
* @param higher
* @param lower
* @return
*/
private static boolean isNotInterfaceImplementation(Method higher, Method lower)
{
return !(declaringClassIsAnInterface(higher) && parametersAreEqual(higher, lower));
}
/**
* check deep equality on parameters of two methods
*
* @param first
* @param second
* @return
*/
private static boolean parametersAreEqual(Method first, Method second)
{
return Arrays.deepEquals(first.getParameterTypes(), second.getParameterTypes());
}
/**
* @param higher
* @param lower
* @return true if return types are equal or covariants
*/
private static boolean returnTypesAreEqualOrCovariant(Method higher, Method lower)
{
return (declaringClassIsAssignableFrom(higher, lower) || higher.getReturnType().equals(lower.getReturnType()));
}
/**
* @param first
* @param second
* @return true if the names of the two methods are equal
*/
private static boolean namesAreEqual(Method first, Method second)
{
return first.getName().equals(second.getName());
}
private static boolean declaringClassIsAnInterface(Method method)
{
return method.getDeclaringClass().isInterface();
}
private static boolean declaringClassIsNotAnInterface(Method method)
{
return !declaringClassIsAnInterface(method);
}
}