Source code
Description: source: package: org.apache.commons.lang3.reflect, class: ConstructorUtils

    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.lang3.reflect;
    019
    020import java.lang.reflect.Constructor;
    021import java.lang.reflect.InvocationTargetException;
    022import java.util.Objects;
    023
    024import org.apache.commons.lang3.ArrayUtils;
    025import org.apache.commons.lang3.ClassUtils;
    026
    027/**
    028 * Utility reflection methods focused on constructors, modeled after {@link MethodUtils}.
    029 *
    030 * <h2>Known Limitations</h2>
    031 * <h3>Accessing Public Constructors In A Default Access Superclass</h3>
    032 * <p>
    033 * There is an issue when invoking {@code public} constructors contained in a default access superclass. Reflection correctly locates these constructors and
    034 * assigns them as {@code public}. However, an {@link IllegalAccessException} is thrown if the constructor is invoked.
    035 * </p>
    036 *
    037 * <p>
    038 * {@link ConstructorUtils} contains a workaround for this situation: it will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
    039 * 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
    040 * security privileges. If this call fails then a warning will be logged and the method may fail.
    041 * </p>
    042 *
    043 * @since 2.5
    044 */
    045public class ConstructorUtils {
    046
    047    /**
    048     * Finds a constructor given a class and signature, checking accessibility.
    049     *
    050     * <p>
    051     * This finds the constructor and ensures that it is accessible. The constructor signature must match the parameter types exactly.
    052     * </p>
    053     *
    054     * @param <T>            the constructor type.
    055     * @param cls            the class to find a constructor for, not {@code null}.
    056     * @param parameterTypes the array of parameter types, {@code null} treated as empty.
    057     * @return the constructor, {@code null} if no matching accessible constructor found.
    058     * @throws NullPointerException if {@code cls} is {@code null}
    059     * @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
    060     *                              for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the
    061     *                              class.
    062     * @see Class#getConstructor
    063     * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
    064     */
    065    public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls, final Class<?>... parameterTypes) {
    066        Objects.requireNonNull(cls, "cls");
    067        try {
    068            return getAccessibleConstructor(cls.getConstructor(parameterTypes));
    069        } catch (final NoSuchMethodException e) {
    070            return null;
    071        }
    072    }
    073
    074    /**
    075     * Checks if the specified constructor is accessible.
    076     *
    077     * <p>
    078     * This simply ensures that the constructor is accessible.
    079     * </p>
    080     *
    081     * @param <T>  the constructor type.
    082     * @param ctor the prototype constructor object, not {@code null}.
    083     * @return the constructor, {@code null} if no matching accessible constructor found.
    084     * @see SecurityManager
    085     * @throws NullPointerException if {@code ctor} is {@code null}
    086     * @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
    087     *                              for a class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
    088     */
    089    public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
    090        Objects.requireNonNull(ctor, "ctor");
    091        return MemberUtils.isAccessible(ctor) && isAccessible(ctor.getDeclaringClass()) ? ctor : null;
    092    }
    093
    094    /**
    095     * Finds an accessible constructor with compatible parameters.
    096     *
    097     * <p>
    098     * This checks all the constructor and finds one with compatible parameters This requires that every parameter is assignable from the given parameter types.
    099     * 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}