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 Constructors In A Non-Public Class</h3> 032 * <p> 033 * Constructors in non-public classes (such as package-private classes or classes enclosed in non-public classes) are 034 * not accessible. Methods such as {@link #getAccessibleConstructor(Class, Class[])} and 035 * {@link #getMatchingAccessibleConstructor(Class, Class[])} return {@code null} when invoked on non-public classes. 036 * Consequently, invocation methods such as {@link #invokeConstructor(Class, Object...)} and 037 * {@link #invokeExactConstructor(Class, Object...)} throw a {@link NoSuchMethodException}. 038 * </p> 039 * 040 * @since 2.5 041 */ 042public class ConstructorUtils { 043 044 /** 045 * Gets a constructor given a class and signature, checking accessibility. 046 * 047 * <p> 048 * This finds the constructor and ensures that it is accessible. The constructor signature must match the parameter types exactly. 049 * </p> 050 * 051 * @param <T> the constructor type. 052 * @param cls The class to find a constructor for, not {@code null}. 053 * @param parameterTypes The array of parameter types, {@code null} treated as empty. 054 * @return The constructor, {@code null} if no matching accessible constructor found. 055 * @throws NullPointerException Thrown if {@code cls} is {@code null}. 056 * @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 057 * for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the 058 * class. 059 * @see Class#getConstructor 060 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 061 */ 062 public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls, final Class<?>... parameterTypes) { 063 Objects.requireNonNull(cls, "cls"); 064 if (!isAccessible(cls)) { 065 return null; 066 } 067 try { 068 return getAccessibleConstructor(cls.getConstructor(parameterTypes)); 069 } catch (final NoSuchMethodException e) { 070 return null; 071 } 072 } 073 074 /** 075 * Gets the specified constructor if it is accessible; otherwise, returns null. 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 Thrown 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 * Gets 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 if (!isAccessible(cls)) { 118 return null; 119 } 120 // see if we can find the constructor directly 121 // most of the time this works and it's much faster 122 try { 123 final Constructor<T> ctor = getAccessibleConstructor(cls.getConstructor(parameterTypes)); 124 if (ctor != null) { 125 return MemberUtils.setAccessibleWorkaround(ctor); 126 } 127 } catch (final NoSuchMethodException ignored) { 128 // ignore 129 } 130 Constructor<T> result = null; 131 /* 132 * (1) Class.getConstructors() is documented to return Constructor<T> so as long as the array is not subsequently modified, everything's fine. 133 */ 134 final Constructor<?>[] ctors = cls.getConstructors(); 135 // return best match: 136 for (Constructor<?> ctor : ctors) { 137 // compare parameters 138 if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) { 139 // get accessible version of constructor 140 ctor = getAccessibleConstructor(ctor); 141 if (ctor != null) { 142 MemberUtils.setAccessibleWorkaround(ctor); 143 if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) { 144 // temporary variable for annotation, see comment above (1) 145 @SuppressWarnings("unchecked") 146 final Constructor<T> constructor = (Constructor<T>) ctor; 147 result = constructor; 148 } 149 } 150 } 151 } 152 return result; 153 } 154 155 /** 156 * Returns a new instance of the specified class inferring the right constructor from the types of the arguments. 157 * 158 * <p> 159 * This locates and calls a constructor. The constructor signature must match the argument types by assignment compatibility. 160 * </p> 161 * 162 * @param <T> the type to be constructed. 163 * @param cls The class to be constructed, not {@code null}. 164 * @param args The array of arguments, {@code null} treated as empty. 165 * @return new instance of {@code cls}, not {@code null}. 166 * @throws NullPointerException Thrown if {@code cls} is {@code null}. 167 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found. 168 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is 169 * inaccessible. 170 * @throws IllegalArgumentException Thrown if: 171 * <ul> 172 * <li>the number of actual and formal parameters differ; or</li> 173 * <li>an unwrapping conversion for primitive arguments fails; or</li> 174 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a 175 * method invocation conversion; if this constructor pertains to an enum type.</li> 176 * </ul> 177 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class. 178 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception. 179 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 180 * @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 181 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the 182 * package of the class. 183 * @see #invokeConstructor(Class, Object[], Class[]) 184 */ 185 public static <T> T invokeConstructor(final Class<T> cls, final Object... args) 186 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 187 final Object[] actuals = ArrayUtils.nullToEmpty(args); 188 return invokeConstructor(cls, actuals, ClassUtils.toClass(actuals)); 189 } 190 191 /** 192 * Returns a new instance of the specified class choosing the right constructor from the list of parameter types. 193 * 194 * <p> 195 * This locates and calls a constructor. The constructor signature must match the parameter types by assignment compatibility. 196 * </p> 197 * 198 * @param <T> the type to be constructed. 199 * @param cls The class to be constructed, not {@code null}. 200 * @param args The array of arguments, {@code null} treated as empty. 201 * @param parameterTypes The array of parameter types, {@code null} treated as empty. 202 * @return new instance of {@code cls}, not {@code null} 203 * @throws NullPointerException Thrown if {@code cls} is {@code null}. 204 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found. 205 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is 206 * inaccessible. 207 * @throws IllegalArgumentException Thrown if: 208 * <ul> 209 * <li>the number of actual and formal parameters differ; or</li> 210 * <li>an unwrapping conversion for primitive arguments fails; or</li> 211 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a 212 * method invocation conversion; if this constructor pertains to an enum type.</li> 213 * </ul> 214 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class. 215 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception. 216 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 217 * @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 218 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the 219 * package of the class. 220 * @see Constructor#newInstance(Object...) 221 * @see Constructor#newInstance 222 */ 223 public static <T> T invokeConstructor(final Class<T> cls, final Object[] args, final Class<?>[] parameterTypes) 224 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 225 final Object[] actuals = ArrayUtils.nullToEmpty(args); 226 final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, ArrayUtils.nullToEmpty(parameterTypes)); 227 if (ctor == null) { 228 throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName()); 229 } 230 return ctor.newInstance(MethodUtils.toVarArgs(ctor, actuals)); 231 } 232 233 /** 234 * Returns a new instance of the specified class inferring the right constructor from the types of the arguments. 235 * 236 * <p> 237 * This locates and calls a constructor. The constructor signature must match the argument types exactly. 238 * </p> 239 * 240 * @param <T> the type to be constructed. 241 * @param cls The class to be constructed, not {@code null}. 242 * @param args The array of arguments, {@code null} treated as empty. 243 * @return new instance of {@code cls}, not {@code null}. 244 * @throws NullPointerException Thrown if {@code cls} is {@code null}. 245 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found. 246 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is 247 * inaccessible. 248 * @throws IllegalArgumentException Thrown if: 249 * <ul> 250 * <li>the number of actual and formal parameters differ; or</li> 251 * <li>an unwrapping conversion for primitive arguments fails; or</li> 252 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a 253 * method invocation conversion; if this constructor pertains to an enum type.</li> 254 * </ul> 255 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class. 256 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception. 257 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 258 * @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 259 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the 260 * package of the class. 261 * @see Constructor#newInstance(Object...) 262 * @see #invokeExactConstructor(Class, Object[], Class[]) 263 */ 264 public static <T> T invokeExactConstructor(final Class<T> cls, final Object... args) 265 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 266 final Object[] actuals = ArrayUtils.nullToEmpty(args); 267 return invokeExactConstructor(cls, actuals, ClassUtils.toClass(actuals)); 268 } 269 270 /** 271 * Returns a new instance of the specified class choosing the right constructor from the list of parameter types. 272 * 273 * <p> 274 * This locates and calls a constructor. The constructor signature must match the parameter types exactly. 275 * </p> 276 * 277 * @param <T> the type to construct. 278 * @param cls The class to construct, not {@code null}. 279 * @param args The array of arguments, {@code null} treated as empty. 280 * @param parameterTypes The array of parameter types, {@code null} treated as empty. 281 * @return new instance of {@code cls}, not {@code null}. 282 * @throws NullPointerException Thrown if {@code cls} is {@code null}. 283 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found. 284 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is 285 * inaccessible. 286 * @throws IllegalArgumentException Thrown if: 287 * <ul> 288 * <li>the number of actual and formal parameters differ; or</li> 289 * <li>an unwrapping conversion for primitive arguments fails; or</li> 290 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a 291 * method invocation conversion; if this constructor pertains to an enum type.</li> 292 * </ul> 293 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class. 294 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception. 295 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 296 * @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 297 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the 298 * package of the class. 299 * @see Constructor#newInstance(Object...) 300 */ 301 public static <T> T invokeExactConstructor(final Class<T> cls, final Object[] args, final Class<?>[] parameterTypes) 302 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 303 final Constructor<T> ctor = getAccessibleConstructor(cls, ArrayUtils.nullToEmpty(parameterTypes)); 304 if (ctor == null) { 305 throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName()); 306 } 307 return ctor.newInstance(ArrayUtils.nullToEmpty(args)); 308 } 309 310 /** 311 * Tests whether the specified class is generally accessible, i.e. is declared in an entirely {@code public} manner. 312 * 313 * @param type to check. 314 * @return {@code true} if {@code type} and any enclosing classes are {@code public}. 315 * @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 316 * class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class. 317 */ 318 private static boolean isAccessible(final Class<?> type) { 319 Class<?> cls = type; 320 while (cls != null) { 321 if (!ClassUtils.isPublic(cls)) { 322 return false; 323 } 324 cls = cls.getEnclosingClass(); 325 } 326 return true; 327 } 328 329 /** 330 * ConstructorUtils instances should NOT be constructed in standard programming. Instead, the class should be used as 331 * {@code ConstructorUtils.invokeConstructor(cls, args)}. 332 * 333 * <p> 334 * This constructor is {@code public} to permit tools that require a JavaBean instance to operate. 335 * </p> 336 * 337 * @deprecated TODO Make private in 4.0. 338 */ 339 @Deprecated 340 public ConstructorUtils() { 341 // empty 342 } 343}