ConstructorUtils.java

  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. package org.apache.commons.lang3.reflect;

  18. import java.lang.reflect.Constructor;
  19. import java.lang.reflect.InvocationTargetException;
  20. import java.util.Objects;

  21. import org.apache.commons.lang3.ArrayUtils;
  22. import org.apache.commons.lang3.ClassUtils;

  23. /**
  24.  *  Utility reflection methods focused on constructors, modeled after
  25.  * {@link MethodUtils}.
  26.  *
  27.  * <h2>Known Limitations</h2>
  28.  * <h3>Accessing Public Constructors In A Default Access Superclass</h3>
  29.  * <p>There is an issue when invoking {@code public} constructors
  30.  * contained in a default access superclass. Reflection correctly locates these
  31.  * constructors and assigns them as {@code public}. However, an
  32.  * {@link IllegalAccessException} is thrown if the constructor is
  33.  * invoked.</p>
  34.  *
  35.  * <p>{@link ConstructorUtils} contains a workaround for this situation: it
  36.  * will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this constructor. If this
  37.  * call succeeds, then the method can be invoked as normal. This call will only
  38.  * succeed when the application has sufficient security privileges. If this call
  39.  * fails then a warning will be logged and the method may fail.</p>
  40.  *
  41.  * @since 2.5
  42.  */
  43. public class ConstructorUtils {

  44.     /**
  45.      * Finds a constructor given a class and signature, checking accessibility.
  46.      *
  47.      * <p>This finds the constructor and ensures that it is accessible.
  48.      * The constructor signature must match the parameter types exactly.</p>
  49.      *
  50.      * @param <T> the constructor type
  51.      * @param cls the class to find a constructor for, not {@code null}
  52.      * @param parameterTypes the array of parameter types, {@code null} treated as empty
  53.      * @return the constructor, {@code null} if no matching accessible constructor found
  54.      * @see Class#getConstructor
  55.      * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
  56.      * @throws NullPointerException if {@code cls} is {@code null}
  57.      */
  58.     public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls,
  59.             final Class<?>... parameterTypes) {
  60.         Objects.requireNonNull(cls, "cls");
  61.         try {
  62.             return getAccessibleConstructor(cls.getConstructor(parameterTypes));
  63.         } catch (final NoSuchMethodException e) {
  64.             return null;
  65.         }
  66.     }

  67.     /**
  68.      * Checks if the specified constructor is accessible.
  69.      *
  70.      * <p>This simply ensures that the constructor is accessible.</p>
  71.      *
  72.      * @param <T> the constructor type
  73.      * @param ctor  the prototype constructor object, not {@code null}
  74.      * @return the constructor, {@code null} if no matching accessible constructor found
  75.      * @see SecurityManager
  76.      * @throws NullPointerException if {@code ctor} is {@code null}
  77.      */
  78.     public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
  79.         Objects.requireNonNull(ctor, "ctor");
  80.         return MemberUtils.isAccessible(ctor)
  81.                 && isAccessible(ctor.getDeclaringClass()) ? ctor : null;
  82.     }

  83.     /**
  84.      * Finds an accessible constructor with compatible parameters.
  85.      *
  86.      * <p>This checks all the constructor and finds one with compatible parameters
  87.      * This requires that every parameter is assignable from the given parameter types.
  88.      * This is a more flexible search than the normal exact matching algorithm.</p>
  89.      *
  90.      * <p>First it checks if there is a constructor matching the exact signature.
  91.      * If not then all the constructors of the class are checked to see if their
  92.      * signatures are assignment-compatible with the parameter types.
  93.      * The first assignment-compatible matching constructor is returned.</p>
  94.      *
  95.      * @param <T> the constructor type
  96.      * @param cls  the class to find a constructor for, not {@code null}
  97.      * @param parameterTypes find method with compatible parameters
  98.      * @return the constructor, null if no matching accessible constructor found
  99.      * @throws NullPointerException if {@code cls} is {@code null}
  100.      * @throws SecurityException if an underlying accessible object's method denies the request.
  101.      * @see SecurityManager#checkPermission
  102.      */
  103.     public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls,
  104.             final Class<?>... parameterTypes) {
  105.         Objects.requireNonNull(cls, "cls");
  106.         // see if we can find the constructor directly
  107.         // most of the time this works and it's much faster
  108.         try {
  109.             return MemberUtils.setAccessibleWorkaround(cls.getConstructor(parameterTypes));
  110.         } catch (final NoSuchMethodException ignored) {
  111.             // ignore
  112.         }
  113.         Constructor<T> result = null;
  114.         /*
  115.          * (1) Class.getConstructors() is documented to return Constructor<T> so as
  116.          * long as the array is not subsequently modified, everything's fine.
  117.          */
  118.         final Constructor<?>[] ctors = cls.getConstructors();

  119.         // return best match:
  120.         for (Constructor<?> ctor : ctors) {
  121.             // compare parameters
  122.             if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) {
  123.                 // get accessible version of constructor
  124.                 ctor = getAccessibleConstructor(ctor);
  125.                 if (ctor != null) {
  126.                     MemberUtils.setAccessibleWorkaround(ctor);
  127.                     if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) {
  128.                         // temporary variable for annotation, see comment above (1)
  129.                         @SuppressWarnings("unchecked")
  130.                         final Constructor<T> constructor = (Constructor<T>) ctor;
  131.                         result = constructor;
  132.                     }
  133.                 }
  134.             }
  135.         }
  136.         return result;
  137.     }

  138.     /**
  139.      * Returns a new instance of the specified class inferring the right constructor
  140.      * from the types of the arguments.
  141.      *
  142.      * <p>This locates and calls a constructor.
  143.      * The constructor signature must match the argument types by assignment compatibility.</p>
  144.      *
  145.      * @param <T> the type to be constructed
  146.      * @param cls  the class to be constructed, not {@code null}
  147.      * @param args  the array of arguments, {@code null} treated as empty
  148.      * @return new instance of {@code cls}, not {@code null}
  149.      * @throws NullPointerException if {@code cls} is {@code null}
  150.      * @throws NoSuchMethodException if a matching constructor cannot be found
  151.      * @throws IllegalAccessException if invocation is not permitted by security
  152.      * @throws InvocationTargetException if an error occurs on invocation
  153.      * @throws InstantiationException if an error occurs on instantiation
  154.      * @throws SecurityException if an underlying accessible object's method denies the request.
  155.      * @see SecurityManager#checkPermission
  156.      * @see #invokeConstructor(Class, Object[], Class[])
  157.      */
  158.     public static <T> T invokeConstructor(final Class<T> cls, Object... args)
  159.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
  160.             InstantiationException {
  161.         args = ArrayUtils.nullToEmpty(args);
  162.         return invokeConstructor(cls, args, ClassUtils.toClass(args));
  163.     }

  164.     /**
  165.      * Returns a new instance of the specified class choosing the right constructor
  166.      * from the list of parameter types.
  167.      *
  168.      * <p>This locates and calls a constructor.
  169.      * The constructor signature must match the parameter types by assignment compatibility.</p>
  170.      *
  171.      * @param <T> the type to be constructed
  172.      * @param cls  the class to be constructed, not {@code null}
  173.      * @param args  the array of arguments, {@code null} treated as empty
  174.      * @param parameterTypes  the array of parameter types, {@code null} treated as empty
  175.      * @return new instance of {@code cls}, not {@code null}
  176.      * @throws NullPointerException if {@code cls} is {@code null}
  177.      * @throws NoSuchMethodException if a matching constructor cannot be found
  178.      * @throws IllegalAccessException if invocation is not permitted by security
  179.      * @throws InvocationTargetException if an error occurs on invocation
  180.      * @throws InstantiationException if an error occurs on instantiation
  181.      * @throws SecurityException if an underlying accessible object's method denies the request.
  182.      * @see SecurityManager#checkPermission
  183.      * @see Constructor#newInstance
  184.      */
  185.     public static <T> T invokeConstructor(final Class<T> cls, Object[] args, Class<?>[] parameterTypes)
  186.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
  187.             InstantiationException {
  188.         args = ArrayUtils.nullToEmpty(args);
  189.         parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
  190.         final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
  191.         if (ctor == null) {
  192.             throw new NoSuchMethodException(
  193.                 "No such accessible constructor on object: " + cls.getName());
  194.         }
  195.         if (ctor.isVarArgs()) {
  196.             final Class<?>[] methodParameterTypes = ctor.getParameterTypes();
  197.             args = MethodUtils.getVarArgs(args, methodParameterTypes);
  198.         }
  199.         return ctor.newInstance(args);
  200.     }

  201.     /**
  202.      * Returns a new instance of the specified class inferring the right constructor
  203.      * from the types of the arguments.
  204.      *
  205.      * <p>This locates and calls a constructor.
  206.      * The constructor signature must match the argument types exactly.</p>
  207.      *
  208.      * @param <T> the type to be constructed
  209.      * @param cls the class to be constructed, not {@code null}
  210.      * @param args the array of arguments, {@code null} treated as empty
  211.      * @return new instance of {@code cls}, not {@code null}
  212.      * @throws NullPointerException if {@code cls} is {@code null}
  213.      * @throws NoSuchMethodException if a matching constructor cannot be found
  214.      * @throws IllegalAccessException if invocation is not permitted by security
  215.      * @throws InvocationTargetException if an error occurs on invocation
  216.      * @throws InstantiationException if an error occurs on instantiation
  217.      * @see #invokeExactConstructor(Class, Object[], Class[])
  218.      */
  219.     public static <T> T invokeExactConstructor(final Class<T> cls, Object... args)
  220.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
  221.             InstantiationException {
  222.         args = ArrayUtils.nullToEmpty(args);
  223.         return invokeExactConstructor(cls, args, ClassUtils.toClass(args));
  224.     }

  225.     /**
  226.      * Returns a new instance of the specified class choosing the right constructor
  227.      * from the list of parameter types.
  228.      *
  229.      * <p>This locates and calls a constructor.
  230.      * The constructor signature must match the parameter types exactly.</p>
  231.      *
  232.      * @param <T> the type to be constructed
  233.      * @param cls the class to be constructed, not {@code null}
  234.      * @param args the array of arguments, {@code null} treated as empty
  235.      * @param parameterTypes  the array of parameter types, {@code null} treated as empty
  236.      * @return new instance of {@code cls}, not {@code null}
  237.      * @throws NullPointerException if {@code cls} is {@code null}
  238.      * @throws NoSuchMethodException if a matching constructor cannot be found
  239.      * @throws IllegalAccessException if invocation is not permitted by security
  240.      * @throws InvocationTargetException if an error occurs on invocation
  241.      * @throws InstantiationException if an error occurs on instantiation
  242.      * @see Constructor#newInstance
  243.      */
  244.     public static <T> T invokeExactConstructor(final Class<T> cls, Object[] args,
  245.             Class<?>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException,
  246.             InvocationTargetException, InstantiationException {
  247.         args = ArrayUtils.nullToEmpty(args);
  248.         parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
  249.         final Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes);
  250.         if (ctor == null) {
  251.             throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName());
  252.         }
  253.         return ctor.newInstance(args);
  254.     }

  255.     /**
  256.      * Tests whether the specified class is generally accessible, i.e. is
  257.      * declared in an entirely {@code public} manner.
  258.      * @param type to check
  259.      * @return {@code true} if {@code type} and any enclosing classes are
  260.      *         {@code public}.
  261.      */
  262.     private static boolean isAccessible(final Class<?> type) {
  263.         Class<?> cls = type;
  264.         while (cls != null) {
  265.             if (!ClassUtils.isPublic(cls)) {
  266.                 return false;
  267.             }
  268.             cls = cls.getEnclosingClass();
  269.         }
  270.         return true;
  271.     }

  272.     /**
  273.      * ConstructorUtils instances should NOT be constructed in standard
  274.      * programming. Instead, the class should be used as
  275.      * {@code ConstructorUtils.invokeConstructor(cls, args)}.
  276.      *
  277.      * <p>This constructor is {@code public} to permit tools that require a JavaBean
  278.      * instance to operate.</p>
  279.      *
  280.      * @deprecated TODO Make private in 4.0.
  281.      */
  282.     @Deprecated
  283.     public ConstructorUtils() {
  284.         // empty
  285.     }

  286. }