If you, like me (who is doing this as part of the Coherence Incubator), are dabbling with Java reflection, you might have run in to the case of trying to match a parameter list to available constructors or methods for a Java class in order to construct objects using reflection.
There is an easy option available in getDeclaredConstructor (and similarily for finding a particular method: getDeclaredMethod) which takes an array of Class objects and maps it to a constructor’s signature. The problem with this admittedly very convenient method is that it does an exact match between the Class for the formal parameter list and the Class of the actual parameter. So if you have a parameter that is of a subclass of the formal parameter, getDeclaredConstructor won’t find it!
The next interesting problem with getDeclaredConstructor is that if your formal parameter list includes any of the primitive types in Java, like byte, int, short, float etc. and your parameter list includes the boxed equivalents then again they won’t match! (for purposes of invoking a constructor or method using reflection, the primitive types are boxed)
But look no further, as part of the latest release of the Coherence Common Incubator project you will find getCompatibleConstructor to the rescue! Below you will find an extract from ReplicationHelper.java that will return a constructor that matches both subtyped parameters as well as primitive types.
/**
* Get a compatible constructor to the supplied parameter types.
*
* @param clazz the class which we want to construct
* @param parameterTypes the types required of the constructor
*
* @return a compatible constructor or null if none exists
*/
public static Constructor<?> getCompatibleConstructor(Class<?> clazz, Class<?>[] parameterTypes)
{
Constructor<?>[] constructors = clazz.getConstructors();
for (int i = 0; i < constructors.length; i++)
{
if (constructors[i].getParameterTypes().length == (parameterTypes != null ? parameterTypes.length : 0))
{
// If we have the same number of parameters there is a shot that we have a compatible
// constructor
Class<?>[] constructorTypes = constructors[i].getParameterTypes();
boolean isCompatible = true;
for (int j = 0; j < (parameterTypes != null ? parameterTypes.length : 0); j++)
{
if (!constructorTypes[j].isAssignableFrom(parameterTypes[j]))
{
// The type is not assignment compatible, however
// we might be able to coerce from a basic type to a boxed type
if (constructorTypes[j].isPrimitive())
{
if (!isAssignablePrimitiveToBoxed(constructorTypes[j], parameterTypes[j]))
{
isCompatible = false;
break;
}
}
}
}
if (isCompatible)
{
return constructors[i];
}
}
}
return null;
}
/**
* <p> Checks if a primitive type is assignable with a boxed type.</p>
*
* @param primitive a primitive class type
* @param boxed a boxed class type
*
* @return true if primitive and boxed are assignment compatible
*/
private static boolean isAssignablePrimitiveToBoxed(Class<?> primitive, Class<?> boxed)
{
if (primitive.equals(java.lang.Boolean.TYPE))
{
if (boxed.equals(java.lang.Boolean.class))
return true;
else
return false;
}
else
{
if (primitive.equals(java.lang.Byte.TYPE))
{
if (boxed.equals(java.lang.Byte.class))
return true;
else
return false;
}
else
{
if (primitive.equals(java.lang.Character.TYPE))
{
if (boxed.equals(java.lang.Character.class))
return true;
else
return false;
}
else
{
if (primitive.equals(java.lang.Double.TYPE))
{
if (boxed.equals(java.lang.Double.class))
return true;
else
return false;
}
else
{
if (primitive.equals(java.lang.Float.TYPE))
{
if (boxed.equals(java.lang.Float.class))
return true;
else
return false;
}
else
{
if (primitive.equals(java.lang.Integer.TYPE))
{
if (boxed.equals(java.lang.Integer.class))
return true;
else
return false;
}
else
{
if (primitive.equals(java.lang.Long.TYPE))
{
if (boxed.equals(java.lang.Long.class))
return true;
else
return false;
}
else
{
if (primitive.equals(java.lang.Short.TYPE))
{
if (boxed.equals(java.lang.Short.class))
return true;
else
return false;
}
}
}
}
}
}
}
}
return false;
}
Man, you saved my day! thank you for sharing this.
The second method could be much shorter:
static boolean isAssignablePrimitiveToBoxed(Class primitive,
Class boxed) {
if (primitive.equals(java.lang.Boolean.TYPE))
return (boxed.equals(java.lang.Boolean.class));
if (primitive.equals(java.lang.Byte.TYPE))
return (boxed.equals(java.lang.Byte.class));
if (primitive.equals(java.lang.Character.TYPE))
return (boxed.equals(java.lang.Character.class));
if (primitive.equals(java.lang.Double.TYPE))
return (boxed.equals(java.lang.Double.class));
if (primitive.equals(java.lang.Float.TYPE))
return (boxed.equals(java.lang.Float.class));
if (primitive.equals(java.lang.Integer.TYPE))
return (boxed.equals(java.lang.Integer.class));
if (primitive.equals(java.lang.Long.TYPE))
return (boxed.equals(java.lang.Long.class));
if (primitive.equals(java.lang.Short.TYPE))
return (boxed.equals(java.lang.Short.class));
return false;
}
This is pretty convenient, but it appears to do a first-compatible match, not a most-compatible one. Been working on my own solution to this problem, but it’s not terribly straightforward.
Yes it does a first compatible match, It doesn’t try to find the most-compatible one (most specific). I’m a bit curious on what is the use case you need most-compatible constructor matching for?
If you would sort the constructor list so that the ones with the most specific parameter types come first in the list I think you would get what you need. Performance is an issue, and caching that sorted list would be helpful…