View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.lang3.reflect;
19  
20  import java.lang.reflect.Constructor;
21  import java.lang.reflect.InvocationTargetException;
22  import java.util.Objects;
23  
24  import org.apache.commons.lang3.ArrayUtils;
25  import org.apache.commons.lang3.ClassUtils;
26  
27  /**
28   * Utility reflection methods focused on constructors, modeled after {@link MethodUtils}.
29   *
30   * <h2>Known Limitations</h2>
31   * <h3>Accessing Public Constructors In A Default Access Superclass</h3>
32   * <p>
33   * There is an issue when invoking {@code public} constructors contained in a default access superclass. Reflection correctly locates these constructors and
34   * assigns them as {@code public}. However, an {@link IllegalAccessException} is thrown if the constructor is invoked.
35   * </p>
36   *
37   * <p>
38   * {@link ConstructorUtils} contains a workaround for this situation: it will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
39   * 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
40   * security privileges. If this call fails then a warning will be logged and the method may fail.
41   * </p>
42   *
43   * @since 2.5
44   */
45  public class ConstructorUtils {
46  
47      /**
48       * Finds a constructor given a class and signature, checking accessibility.
49       *
50       * <p>
51       * This finds the constructor and ensures that it is accessible. The constructor signature must match the parameter types exactly.
52       * </p>
53       *
54       * @param <T>            the constructor type.
55       * @param cls            the class to find a constructor for, not {@code null}.
56       * @param parameterTypes the array of parameter types, {@code null} treated as empty.
57       * @return the constructor, {@code null} if no matching accessible constructor found.
58       * @throws NullPointerException if {@code cls} is {@code null}
59       * @throws SecurityException    Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class loader
60       *                              for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the
61       *                              class.
62       * @see Class#getConstructor
63       * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
64       */
65      public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls, final Class<?>... parameterTypes) {
66          Objects.requireNonNull(cls, "cls");
67          try {
68              return getAccessibleConstructor(cls.getConstructor(parameterTypes));
69          } catch (final NoSuchMethodException e) {
70              return null;
71          }
72      }
73  
74      /**
75       * Checks if the specified constructor is accessible.
76       *
77       * <p>
78       * This simply ensures that the constructor is accessible.
79       * </p>
80       *
81       * @param <T>  the constructor type.
82       * @param ctor the prototype constructor object, not {@code null}.
83       * @return the constructor, {@code null} if no matching accessible constructor found.
84       * @see SecurityManager
85       * @throws NullPointerException if {@code ctor} is {@code null}
86       * @throws SecurityException    Thrown if a security manager is present and a caller's class loader is not the same as or an ancestor of the class loader
87       *                              for a class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
88       */
89      public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
90          Objects.requireNonNull(ctor, "ctor");
91          return MemberUtils.isAccessible(ctor) && isAccessible(ctor.getDeclaringClass()) ? ctor : null;
92      }
93  
94      /**
95       * Finds an accessible constructor with compatible parameters.
96       *
97       * <p>
98       * This checks all the constructor and finds one with compatible parameters This requires that every parameter is assignable from the given parameter types.
99       * This is a more flexible search than the normal exact matching algorithm.
100      * </p>
101      * <p>
102      * First it checks if there is a constructor matching the exact signature. If not then all the constructors of the class are checked to see if their
103      * signatures are assignment-compatible with the parameter types. The first assignment-compatible matching constructor is returned.
104      * </p>
105      *
106      * @param <T>            the constructor type.
107      * @param cls            the class to find a constructor for, not {@code null}.
108      * @param parameterTypes find method with compatible parameters.
109      * @return the constructor, null if no matching accessible constructor found.
110      * @throws NullPointerException Thrown if {@code cls} is {@code null}
111      * @throws SecurityException    Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class loader for the
112      *                              class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
113      * @see SecurityManager#checkPackageAccess(String)
114      */
115     public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls, final Class<?>... parameterTypes) {
116         Objects.requireNonNull(cls, "cls");
117         // see if we can find the constructor directly
118         // most of the time this works and it's much faster
119         try {
120             return MemberUtils.setAccessibleWorkaround(cls.getConstructor(parameterTypes));
121         } catch (final NoSuchMethodException ignored) {
122             // ignore
123         }
124         Constructor<T> result = null;
125         /*
126          * (1) Class.getConstructors() is documented to return Constructor<T> so as long as the array is not subsequently modified, everything's fine.
127          */
128         final Constructor<?>[] ctors = cls.getConstructors();
129         // return best match:
130         for (Constructor<?> ctor : ctors) {
131             // compare parameters
132             if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) {
133                 // get accessible version of constructor
134                 ctor = getAccessibleConstructor(ctor);
135                 if (ctor != null) {
136                     MemberUtils.setAccessibleWorkaround(ctor);
137                     if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) {
138                         // temporary variable for annotation, see comment above (1)
139                         @SuppressWarnings("unchecked")
140                         final Constructor<T> constructor = (Constructor<T>) ctor;
141                         result = constructor;
142                     }
143                 }
144             }
145         }
146         return result;
147     }
148 
149     /**
150      * Returns a new instance of the specified class inferring the right constructor from the types of the arguments.
151      *
152      * <p>
153      * This locates and calls a constructor. The constructor signature must match the argument types by assignment compatibility.
154      * </p>
155      *
156      * @param <T>  the type to be constructed.
157      * @param cls  the class to be constructed, not {@code null}.
158      * @param args the array of arguments, {@code null} treated as empty.
159      * @return new instance of {@code cls}, not {@code null}.
160      * @throws NullPointerException        Thrown if {@code cls} is {@code null}.
161      * @throws NoSuchMethodException       Thrown if a matching constructor cannot be found.
162      * @throws IllegalAccessException      Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
163      *                                     inaccessible.
164      * @throws IllegalArgumentException    Thrown if:
165      *                                     <ul>
166      *                                     <li>the number of actual and formal parameters differ; or</li>
167      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
168      *                                     <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
169      *                                     method invocation conversion; if this constructor pertains to an enum type.
170      *                                     </ul>
171      * @throws InstantiationException      Thrown if the class that declares the underlying constructor represents an abstract class.
172      * @throws InvocationTargetException   Thrown if the underlying constructor throws an exception.
173      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
174      * @throws SecurityException           Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
175      *                                     loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
176      *                                     package of the class.
177      * @see #invokeConstructor(Class, Object[], Class[])
178      */
179     public static <T> T invokeConstructor(final Class<T> cls, final Object... args)
180             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
181         final Object[] actuals = ArrayUtils.nullToEmpty(args);
182         return invokeConstructor(cls, actuals, ClassUtils.toClass(actuals));
183     }
184 
185     /**
186      * Returns a new instance of the specified class choosing the right constructor from the list of parameter types.
187      *
188      * <p>
189      * This locates and calls a constructor. The constructor signature must match the parameter types by assignment compatibility.
190      * </p>
191      *
192      * @param <T>            the type to be constructed.
193      * @param cls            the class to be constructed, not {@code null}.
194      * @param args           the array of arguments, {@code null} treated as empty.
195      * @param parameterTypes the array of parameter types, {@code null} treated as empty.
196      * @return new instance of {@code cls}, not {@code null}
197      * @throws NullPointerException        Thrown if {@code cls} is {@code null}.
198      * @throws NoSuchMethodException       Thrown if a matching constructor cannot be found.
199      * @throws IllegalAccessException      Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
200      *                                     inaccessible.
201      * @throws IllegalArgumentException    Thrown if:
202      *                                     <ul>
203      *                                     <li>the number of actual and formal parameters differ; or</li>
204      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
205      *                                     <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
206      *                                     method invocation conversion; if this constructor pertains to an enum type.
207      *                                     </ul>
208      * @throws InstantiationException      Thrown if the class that declares the underlying constructor represents an abstract class.
209      * @throws InvocationTargetException   Thrown if the underlying constructor throws an exception.
210      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
211      * @throws SecurityException           Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
212      *                                     loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
213      *                                     package of the class.
214      * @see Constructor#newInstance(Object...)
215      * @see Constructor#newInstance
216      */
217     public static <T> T invokeConstructor(final Class<T> cls, final Object[] args, final Class<?>[] parameterTypes)
218             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
219         final Object[] actuals = ArrayUtils.nullToEmpty(args);
220         final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, ArrayUtils.nullToEmpty(parameterTypes));
221         if (ctor == null) {
222             throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName());
223         }
224         return ctor.newInstance(MethodUtils.toVarArgs(ctor, actuals));
225     }
226 
227     /**
228      * Returns a new instance of the specified class inferring the right constructor from the types of the arguments.
229      *
230      * <p>
231      * This locates and calls a constructor. The constructor signature must match the argument types exactly.
232      * </p>
233      *
234      * @param <T>  the type to be constructed.
235      * @param cls  the class to be constructed, not {@code null}.
236      * @param args the array of arguments, {@code null} treated as empty.
237      * @return new instance of {@code cls}, not {@code null}.
238      * @throws NullPointerException        Thrown if {@code cls} is {@code null}.
239      * @throws NoSuchMethodException       Thrown if a matching constructor cannot be found.
240      * @throws IllegalAccessException      Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
241      *                                     inaccessible.
242      * @throws IllegalArgumentException    Thrown if:
243      *                                     <ul>
244      *                                     <li>the number of actual and formal parameters differ; or</li>
245      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
246      *                                     <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
247      *                                     method invocation conversion; if this constructor pertains to an enum type.
248      *                                     </ul>
249      * @throws InstantiationException      Thrown if the class that declares the underlying constructor represents an abstract class.
250      * @throws InvocationTargetException   Thrown if the underlying constructor throws an exception.
251      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
252      * @throws SecurityException           Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
253      *                                     loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
254      *                                     package of the class.
255      * @see Constructor#newInstance(Object...)
256      * @see #invokeExactConstructor(Class, Object[], Class[])
257      */
258     public static <T> T invokeExactConstructor(final Class<T> cls, final Object... args)
259             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
260         final Object[] actuals = ArrayUtils.nullToEmpty(args);
261         return invokeExactConstructor(cls, actuals, ClassUtils.toClass(actuals));
262     }
263 
264     /**
265      * Returns a new instance of the specified class choosing the right constructor from the list of parameter types.
266      *
267      * <p>
268      * This locates and calls a constructor. The constructor signature must match the parameter types exactly.
269      * </p>
270      *
271      * @param <T>            the type to construct.
272      * @param cls            the class to construct, not {@code null}.
273      * @param args           the array of arguments, {@code null} treated as empty.
274      * @param parameterTypes the array of parameter types, {@code null} treated as empty.
275      * @return new instance of {@code cls}, not {@code null}.
276      * @throws NullPointerException        Thrown if {@code cls} is {@code null}.
277      * @throws NoSuchMethodException       Thrown if a matching constructor cannot be found.
278      * @throws IllegalAccessException      Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
279      *                                     inaccessible.
280      * @throws IllegalArgumentException    Thrown if:
281      *                                     <ul>
282      *                                     <li>the number of actual and formal parameters differ; or</li>
283      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
284      *                                     <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
285      *                                     method invocation conversion; if this constructor pertains to an enum type.
286      *                                     </ul>
287      * @throws InstantiationException      Thrown if the class that declares the underlying constructor represents an abstract class.
288      * @throws InvocationTargetException   Thrown if the underlying constructor throws an exception.
289      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
290      * @throws SecurityException           Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
291      *                                     loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
292      *                                     package of the class.
293      * @see Constructor#newInstance(Object...)
294      */
295     public static <T> T invokeExactConstructor(final Class<T> cls, final Object[] args, final Class<?>[] parameterTypes)
296             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
297         final Constructor<T> ctor = getAccessibleConstructor(cls, ArrayUtils.nullToEmpty(parameterTypes));
298         if (ctor == null) {
299             throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName());
300         }
301         return ctor.newInstance(ArrayUtils.nullToEmpty(args));
302     }
303 
304     /**
305      * Tests whether the specified class is generally accessible, i.e. is declared in an entirely {@code public} manner.
306      *
307      * @param type to check.
308      * @return {@code true} if {@code type} and any enclosing classes are {@code public}.
309      * @throws SecurityException Thrown if a security manager is present and a caller's class loader is not the same as or an ancestor of the class loader for a
310      *                           class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
311      */
312     private static boolean isAccessible(final Class<?> type) {
313         Class<?> cls = type;
314         while (cls != null) {
315             if (!ClassUtils.isPublic(cls)) {
316                 return false;
317             }
318             cls = cls.getEnclosingClass();
319         }
320         return true;
321     }
322 
323     /**
324      * ConstructorUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
325      * {@code ConstructorUtils.invokeConstructor(cls, args)}.
326      *
327      * <p>
328      * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
329      * </p>
330      *
331      * @deprecated TODO Make private in 4.0.
332      */
333     @Deprecated
334     public ConstructorUtils() {
335         // empty
336     }
337 }