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}