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 19 import java.lang.reflect.Constructor; 20 import java.lang.reflect.InvocationTargetException; 21 import java.util.Objects; 22 23 import org.apache.commons.lang3.ArrayUtils; 24 import org.apache.commons.lang3.ClassUtils; 25 26 /** 27 * Utility reflection methods focused on constructors, modeled after 28 * {@link MethodUtils}. 29 * 30 * <h2>Known Limitations</h2> 31 * <h3>Accessing Public Constructors In A Default Access Superclass</h3> 32 * <p>There is an issue when invoking {@code public} constructors 33 * contained in a default access superclass. Reflection correctly locates these 34 * constructors and assigns them as {@code public}. However, an 35 * {@link IllegalAccessException} is thrown if the constructor is 36 * invoked.</p> 37 * 38 * <p>{@link ConstructorUtils} contains a workaround for this situation: it 39 * will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this constructor. If this 40 * call succeeds, then the method can be invoked as normal. This call will only 41 * succeed when the application has sufficient security privileges. If this call 42 * fails then a warning will be logged and the method may fail.</p> 43 * 44 * @since 2.5 45 */ 46 public class ConstructorUtils { 47 48 /** 49 * Finds a constructor given a class and signature, checking accessibility. 50 * 51 * <p>This finds the constructor and ensures that it is accessible. 52 * The constructor signature must match the parameter types exactly.</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 * @see Class#getConstructor 59 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 60 * @throws NullPointerException if {@code cls} is {@code null} 61 */ 62 public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls, 63 final Class<?>... parameterTypes) { 64 Objects.requireNonNull(cls, "cls"); 65 try { 66 return getAccessibleConstructor(cls.getConstructor(parameterTypes)); 67 } catch (final NoSuchMethodException e) { 68 return null; 69 } 70 } 71 72 /** 73 * Checks if the specified constructor is accessible. 74 * 75 * <p>This simply ensures that the constructor is accessible.</p> 76 * 77 * @param <T> the constructor type 78 * @param ctor the prototype constructor object, not {@code null} 79 * @return the constructor, {@code null} if no matching accessible constructor found 80 * @see SecurityManager 81 * @throws NullPointerException if {@code ctor} is {@code null} 82 */ 83 public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) { 84 Objects.requireNonNull(ctor, "ctor"); 85 return MemberUtils.isAccessible(ctor) 86 && isAccessible(ctor.getDeclaringClass()) ? ctor : null; 87 } 88 89 /** 90 * Finds an accessible constructor with compatible parameters. 91 * 92 * <p>This checks all the constructor and finds one with compatible parameters 93 * This requires that every parameter is assignable from the given parameter types. 94 * This is a more flexible search than the normal exact matching algorithm.</p> 95 * 96 * <p>First it checks if there is a constructor matching the exact signature. 97 * If not then all the constructors of the class are checked to see if their 98 * signatures are assignment-compatible with the parameter types. 99 * The first assignment-compatible matching constructor is returned.</p> 100 * 101 * @param <T> the constructor type 102 * @param cls the class to find a constructor for, not {@code null} 103 * @param parameterTypes find method with compatible parameters 104 * @return the constructor, null if no matching accessible constructor found 105 * @throws NullPointerException if {@code cls} is {@code null} 106 * @throws SecurityException if an underlying accessible object's method denies the request. 107 * @see SecurityManager#checkPermission 108 */ 109 public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls, 110 final Class<?>... parameterTypes) { 111 Objects.requireNonNull(cls, "cls"); 112 // see if we can find the constructor directly 113 // most of the time this works and it's much faster 114 try { 115 return MemberUtils.setAccessibleWorkaround(cls.getConstructor(parameterTypes)); 116 } catch (final NoSuchMethodException ignored) { 117 // ignore 118 } 119 Constructor<T> result = null; 120 /* 121 * (1) Class.getConstructors() is documented to return Constructor<T> so as 122 * long as the array is not subsequently modified, everything's fine. 123 */ 124 final Constructor<?>[] ctors = cls.getConstructors(); 125 126 // return best match: 127 for (Constructor<?> ctor : ctors) { 128 // compare parameters 129 if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) { 130 // get accessible version of constructor 131 ctor = getAccessibleConstructor(ctor); 132 if (ctor != null) { 133 MemberUtils.setAccessibleWorkaround(ctor); 134 if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) { 135 // temporary variable for annotation, see comment above (1) 136 @SuppressWarnings("unchecked") 137 final Constructor<T> constructor = (Constructor<T>) ctor; 138 result = constructor; 139 } 140 } 141 } 142 } 143 return result; 144 } 145 146 /** 147 * Returns a new instance of the specified class inferring the right constructor 148 * from the types of the arguments. 149 * 150 * <p>This locates and calls a constructor. 151 * The constructor signature must match the argument types by assignment compatibility.</p> 152 * 153 * @param <T> the type to be constructed 154 * @param cls the class to be constructed, not {@code null} 155 * @param args the array of arguments, {@code null} treated as empty 156 * @return new instance of {@code cls}, not {@code null} 157 * @throws NullPointerException if {@code cls} is {@code null} 158 * @throws NoSuchMethodException if a matching constructor cannot be found 159 * @throws IllegalAccessException if invocation is not permitted by security 160 * @throws InvocationTargetException if an error occurs on invocation 161 * @throws InstantiationException if an error occurs on instantiation 162 * @throws SecurityException if an underlying accessible object's method denies the request. 163 * @see SecurityManager#checkPermission 164 * @see #invokeConstructor(Class, Object[], Class[]) 165 */ 166 public static <T> T invokeConstructor(final Class<T> cls, Object... args) 167 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, 168 InstantiationException { 169 args = ArrayUtils.nullToEmpty(args); 170 return invokeConstructor(cls, args, ClassUtils.toClass(args)); 171 } 172 173 /** 174 * Returns a new instance of the specified class choosing the right constructor 175 * from the list of parameter types. 176 * 177 * <p>This locates and calls a constructor. 178 * The constructor signature must match the parameter types by assignment compatibility.</p> 179 * 180 * @param <T> the type to be constructed 181 * @param cls the class to be constructed, not {@code null} 182 * @param args the array of arguments, {@code null} treated as empty 183 * @param parameterTypes the array of parameter types, {@code null} treated as empty 184 * @return new instance of {@code cls}, not {@code null} 185 * @throws NullPointerException if {@code cls} is {@code null} 186 * @throws NoSuchMethodException if a matching constructor cannot be found 187 * @throws IllegalAccessException if invocation is not permitted by security 188 * @throws InvocationTargetException if an error occurs on invocation 189 * @throws InstantiationException if an error occurs on instantiation 190 * @throws SecurityException if an underlying accessible object's method denies the request. 191 * @see SecurityManager#checkPermission 192 * @see Constructor#newInstance 193 */ 194 public static <T> T invokeConstructor(final Class<T> cls, Object[] args, Class<?>[] parameterTypes) 195 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, 196 InstantiationException { 197 args = ArrayUtils.nullToEmpty(args); 198 parameterTypes = ArrayUtils.nullToEmpty(parameterTypes); 199 final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, parameterTypes); 200 if (ctor == null) { 201 throw new NoSuchMethodException( 202 "No such accessible constructor on object: " + cls.getName()); 203 } 204 if (ctor.isVarArgs()) { 205 final Class<?>[] methodParameterTypes = ctor.getParameterTypes(); 206 args = MethodUtils.getVarArgs(args, methodParameterTypes); 207 } 208 return ctor.newInstance(args); 209 } 210 211 /** 212 * Returns a new instance of the specified class inferring the right constructor 213 * from the types of the arguments. 214 * 215 * <p>This locates and calls a constructor. 216 * The constructor signature must match the argument types exactly.</p> 217 * 218 * @param <T> the type to be constructed 219 * @param cls the class to be constructed, not {@code null} 220 * @param args the array of arguments, {@code null} treated as empty 221 * @return new instance of {@code cls}, not {@code null} 222 * @throws NullPointerException if {@code cls} is {@code null} 223 * @throws NoSuchMethodException if a matching constructor cannot be found 224 * @throws IllegalAccessException if invocation is not permitted by security 225 * @throws InvocationTargetException if an error occurs on invocation 226 * @throws InstantiationException if an error occurs on instantiation 227 * @see #invokeExactConstructor(Class, Object[], Class[]) 228 */ 229 public static <T> T invokeExactConstructor(final Class<T> cls, Object... args) 230 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, 231 InstantiationException { 232 args = ArrayUtils.nullToEmpty(args); 233 return invokeExactConstructor(cls, args, ClassUtils.toClass(args)); 234 } 235 236 /** 237 * Returns a new instance of the specified class choosing the right constructor 238 * from the list of parameter types. 239 * 240 * <p>This locates and calls a constructor. 241 * The constructor signature must match the parameter types exactly.</p> 242 * 243 * @param <T> the type to be constructed 244 * @param cls the class to be constructed, not {@code null} 245 * @param args the array of arguments, {@code null} treated as empty 246 * @param parameterTypes the array of parameter types, {@code null} treated as empty 247 * @return new instance of {@code cls}, not {@code null} 248 * @throws NullPointerException if {@code cls} is {@code null} 249 * @throws NoSuchMethodException if a matching constructor cannot be found 250 * @throws IllegalAccessException if invocation is not permitted by security 251 * @throws InvocationTargetException if an error occurs on invocation 252 * @throws InstantiationException if an error occurs on instantiation 253 * @see Constructor#newInstance 254 */ 255 public static <T> T invokeExactConstructor(final Class<T> cls, Object[] args, 256 Class<?>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, 257 InvocationTargetException, InstantiationException { 258 args = ArrayUtils.nullToEmpty(args); 259 parameterTypes = ArrayUtils.nullToEmpty(parameterTypes); 260 final Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes); 261 if (ctor == null) { 262 throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName()); 263 } 264 return ctor.newInstance(args); 265 } 266 267 /** 268 * Tests whether the specified class is generally accessible, i.e. is 269 * declared in an entirely {@code public} manner. 270 * @param type to check 271 * @return {@code true} if {@code type} and any enclosing classes are 272 * {@code public}. 273 */ 274 private static boolean isAccessible(final Class<?> type) { 275 Class<?> cls = type; 276 while (cls != null) { 277 if (!ClassUtils.isPublic(cls)) { 278 return false; 279 } 280 cls = cls.getEnclosingClass(); 281 } 282 return true; 283 } 284 285 /** 286 * ConstructorUtils instances should NOT be constructed in standard 287 * programming. Instead, the class should be used as 288 * {@code ConstructorUtils.invokeConstructor(cls, args)}. 289 * 290 * <p>This constructor is {@code public} to permit tools that require a JavaBean 291 * instance to operate.</p> 292 * 293 * @deprecated TODO Make private in 4.0. 294 */ 295 @Deprecated 296 public ConstructorUtils() { 297 // empty 298 } 299 300 }