diff --git a/src/main/java/org/apache/commons/lang3/reflect/ConstructorUtils.java b/src/main/java/org/apache/commons/lang3/reflect/ConstructorUtils.java index b893b3c65..e11f10a82 100644 --- a/src/main/java/org/apache/commons/lang3/reflect/ConstructorUtils.java +++ b/src/main/java/org/apache/commons/lang3/reflect/ConstructorUtils.java @@ -24,20 +24,22 @@ import org.apache.commons.lang3.ClassUtils; /** - *

Utility reflection methods focussed on constructors, modelled after {@link MethodUtils}.

- * - *

Known Limitations

- *

Accessing Public Constructors In A Default Access Superclass

- *

There is an issue when invoking public constructors contained in a default access superclass. - * Reflection locates these constructors fine and correctly assigns them as public. - * However, an IllegalAccessException is thrown if the constructors is invoked.

- * - *

ConstructorUtils contains a workaround for this situation. - * It will attempt to call setAccessible on this constructor. - * If this call succeeds, then the method can be invoked as normal. - * This call will only succeed when the application has sufficient security privilages. - * If this call fails then a warning will be logged and the method may fail.

- * + *

Utility reflection methods focussed on constructors, modelled after + * {@link MethodUtils}.

+ * + *

Known Limitations

Accessing Public Constructors In A Default + * Access Superclass

There is an issue when invoking public constructors + * contained in a default access superclass. Reflection locates these + * constructors fine and correctly assigns them as public. However, an + * IllegalAccessException is thrown if the constructors is + * invoked.

+ * + *

ConstructorUtils contains a workaround for this situation. It + * will attempt to call setAccessible on this constructor. If this + * call succeeds, then the method can be invoked as normal. This call will only + * succeed when the application has sufficient security privilages. If this call + * fails then a warning will be logged and the method may fail.

+ * * @author Apache Software Foundation * @author Craig R. McClanahan * @author Ralph Schaer @@ -53,10 +55,10 @@ public class ConstructorUtils { /** - *

ConstructorUtils instances should NOT be constructed in standard programming. - * Instead, the class should be used as + *

ConstructorUtils instances should NOT be constructed in standard + * programming. Instead, the class should be used as * ConstructorUtils.invokeConstructor(cls, args).

- * + * *

This constructor is public to permit tools that require a JavaBean * instance to operate.

*/ @@ -65,26 +67,31 @@ public ConstructorUtils() { } /** - *

Returns new instance of klazz created using the actual arguments args. - * The formal parameter types are inferred from the actual values of args. - * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.

- * + *

Returns new instance of klazz created using the actual + * arguments args. The formal parameter types are inferred from + * the actual values of args. See + * {@link #invokeExactConstructor(Class, Object[], Class[])} for more + * details.

+ * *

The signatures should be assignment compatible.

- * + * * @param cls the class to be constructed. * @param args actual argument array * @return new instance of klazz - * + * * @throws NoSuchMethodException If the constructor cannot be found - * @throws IllegalAccessException If an error occurs accessing the constructor - * @throws InvocationTargetException If an error occurs invoking the constructor + * @throws IllegalAccessException If an error occurs accessing the + * constructor + * @throws InvocationTargetException If an error occurs invoking the + * constructor * @throws InstantiationException If an error occurs instantiating the class - * - * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) + * + * @see #invokeConstructor(java.lang.Class, java.lang.Object[], + * java.lang.Class[]) */ public static T invokeConstructor(Class cls, Object... args) - throws NoSuchMethodException, IllegalAccessException, - InvocationTargetException, InstantiationException { + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, + InstantiationException { if (null == args) { args = ArrayUtils.EMPTY_OBJECT_ARRAY; } @@ -97,24 +104,24 @@ public static T invokeConstructor(Class cls, Object... args) /** *

Returns new instance of klazz created using constructor - * with signature parameterTypes and actual arguments args.

- * + * with signature parameterTypes and actual arguments + * args.

+ * *

The signatures should be assignment compatible.

- * + * * @param cls the class to be constructed. * @param args actual argument array * @param parameterTypes parameter types array * @return new instance of klazz - * + * * @throws NoSuchMethodException if matching constructor cannot be found * @throws IllegalAccessException thrown on the constructor's invocation * @throws InvocationTargetException thrown on the constructor's invocation * @throws InstantiationException thrown on the constructor's invocation * @see Constructor#newInstance */ - public static T invokeConstructor(Class cls, Object[] args, - Class[] parameterTypes) throws NoSuchMethodException, - IllegalAccessException, InvocationTargetException, + public static T invokeConstructor(Class cls, Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { if (parameterTypes == null) { parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; @@ -124,34 +131,38 @@ public static T invokeConstructor(Class cls, Object[] args, } Constructor ctor = getMatchingAccessibleConstructor(cls, parameterTypes); if (null == ctor) { - throw new NoSuchMethodException( - "No such accessible constructor on object: " - + cls.getName()); + throw new NoSuchMethodException("No such accessible constructor on object: " + + cls.getName()); } return ctor.newInstance(args); } /** - *

Returns new instance of klazz created using the actual arguments args. - * The formal parameter types are inferred from the actual values of args. - * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.

- * + *

Returns new instance of klazz created using the actual + * arguments args. The formal parameter types are inferred from + * the actual values of args. See + * {@link #invokeExactConstructor(Class, Object[], Class[])} for more + * details.

+ * *

The signatures should match exactly.

- * + * * @param cls the class to be constructed. * @param args actual argument array * @return new instance of klazz - * + * * @throws NoSuchMethodException If the constructor cannot be found - * @throws IllegalAccessException If an error occurs accessing the constructor - * @throws InvocationTargetException If an error occurs invoking the constructor + * @throws IllegalAccessException If an error occurs accessing the + * constructor + * @throws InvocationTargetException If an error occurs invoking the + * constructor * @throws InstantiationException If an error occurs instantiating the class - * - * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) + * + * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], + * java.lang.Class[]) */ public static T invokeExactConstructor(Class cls, Object... args) - throws NoSuchMethodException, IllegalAccessException, - InvocationTargetException, InstantiationException { + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, + InstantiationException { if (null == args) { args = ArrayUtils.EMPTY_OBJECT_ARRAY; } @@ -167,14 +178,14 @@ public static T invokeExactConstructor(Class cls, Object... args) *

Returns new instance of klazz created using constructor * with signature parameterTypes and actual arguments * args.

- * + * *

The signatures should match exactly.

- * + * * @param cls the class to be constructed. * @param args actual argument array * @param parameterTypes parameter types array * @return new instance of klazz - * + * * @throws NoSuchMethodException if matching constructor cannot be found * @throws IllegalAccessException thrown on the constructor's invocation * @throws InvocationTargetException thrown on the constructor's invocation @@ -182,9 +193,8 @@ public static T invokeExactConstructor(Class cls, Object... args) * @see Constructor#newInstance */ public static T invokeExactConstructor(Class cls, Object[] args, - Class[] parameterTypes) throws NoSuchMethodException, - IllegalAccessException, InvocationTargetException, - InstantiationException { + Class[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException, InstantiationException { if (args == null) { args = ArrayUtils.EMPTY_OBJECT_ARRAY; } @@ -193,9 +203,8 @@ public static T invokeExactConstructor(Class cls, Object[] args, } Constructor ctor = getAccessibleConstructor(cls, parameterTypes); if (null == ctor) { - throw new NoSuchMethodException( - "No such accessible constructor on object: " - + cls.getName()); + throw new NoSuchMethodException("No such accessible constructor on object: " + + cls.getName()); } return ctor.newInstance(args); } @@ -225,24 +234,24 @@ public static Constructor getAccessibleConstructor(Class cls, */ public static Constructor getAccessibleConstructor(Constructor ctor) { return MemberUtils.isAccessible(ctor) - && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor - : null; + && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor : null; } /** - *

Find an accessible constructor with compatible parameters. - * Compatible parameters mean that every method parameter is assignable from - * the given parameters. In other words, it finds constructor that will take - * the parameters given.

- * + *

Find an accessible constructor with compatible parameters. Compatible + * parameters mean that every method parameter is assignable from the given + * parameters. In other words, it finds constructor that will take the + * parameters given.

+ * *

First it checks if there is constructor matching the exact signature. - * If no such, all the constructors of the class are tested if their signatures - * are assignment compatible with the parameter types. - * The first matching constructor is returned.

- * + * If no such, all the constructors of the class are tested if their + * signatures are assignment compatible with the parameter types. The first + * matching constructor is returned.

+ * * @param cls find constructor for this class * @param parameterTypes find method with compatible parameters - * @return a valid Constructor object. If there's no matching constructor, returns null. + * @return a valid Constructor object. If there's no matching constructor, + * returns null. */ public static Constructor getMatchingAccessibleConstructor(Class cls, Class... parameterTypes) { @@ -255,20 +264,23 @@ public static Constructor getMatchingAccessibleConstructor(Class cls, } catch (NoSuchMethodException e) { /* SWALLOW */ } Constructor result = null; - // search through all constructors - Constructor[] ctors = cls.getConstructors(); - for (int i = 0; i < ctors.length; i++) { + /* + * Class.getConstructors() is documented to return Constructor so as + * long as the array is not subsequently modified, everything's fine: + */ + @SuppressWarnings("unchecked") + Constructor[] ctors = (Constructor[]) cls.getConstructors(); + + // return best match: + for (Constructor ctor : ctors) { // compare parameters - if (ClassUtils.isAssignable(parameterTypes, ctors[i] - .getParameterTypes(), true)) { - // get accessible version of method - @SuppressWarnings("unchecked") // TODO is this OK? If so, why? - Constructor ctor = getAccessibleConstructor((Constructor) ctors[i]); + if (ClassUtils.isAssignable(parameterTypes, ctor.getParameterTypes(), true)) { + // get accessible version of constructor + ctor = getAccessibleConstructor(ctor); if (ctor != null) { MemberUtils.setAccessibleWorkaround(ctor); if (result == null - || MemberUtils.compareParameterTypes(ctor - .getParameterTypes(), result + || MemberUtils.compareParameterTypes(ctor.getParameterTypes(), result .getParameterTypes(), parameterTypes) < 0) { result = ctor; }