• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *      http://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.annotation.Annotation;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.Modifier;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Objects;
26 import java.util.stream.Collectors;
27 
28 import org.apache.commons.lang3.ArrayUtils;
29 import org.apache.commons.lang3.ClassUtils;
30 import org.apache.commons.lang3.JavaVersion;
31 import org.apache.commons.lang3.StringUtils;
32 import org.apache.commons.lang3.SystemUtils;
33 import org.apache.commons.lang3.Validate;
34 
35 /**
36  * Utilities for working with {@link Field}s by reflection. Adapted and refactored from the dormant [reflect] Commons
37  * sandbox component.
38  * <p>
39  * The ability is provided to break the scoping restrictions coded by the programmer. This can allow fields to be
40  * changed that shouldn't be. This facility should be used with care.
41  * </p>
42  * @since 2.5
43  */
44 public class FieldUtils {
45 
46     /**
47      * {@link FieldUtils} instances should NOT be constructed in standard programming.
48      * <p>
49      * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
50      * </p>
51      */
FieldUtils()52     public FieldUtils() {
53     }
54 
55     /**
56      * Gets an accessible {@link Field} by name respecting scope. Superclasses/interfaces will be considered.
57      *
58      * @param cls
59      *            the {@link Class} to reflect, must not be {@code null}
60      * @param fieldName
61      *            the field name to obtain
62      * @return the Field object
63      * @throws IllegalArgumentException
64      *             if the class is {@code null}, or the field name is blank or empty
65      */
getField(final Class<?> cls, final String fieldName)66     public static Field getField(final Class<?> cls, final String fieldName) {
67         return MemberUtils.setAccessibleWorkaround(getField(cls, fieldName, false));
68     }
69 
70     /**
71      * Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be
72      * considered.
73      *
74      * @param cls
75      *            the {@link Class} to reflect, must not be {@code null}
76      * @param fieldName
77      *            the field name to obtain
78      * @param forceAccess
79      *            whether to break scope restrictions using the
80      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
81      *            match {@code public} fields.
82      * @return the Field object
83      * @throws NullPointerException if the class is {@code null}
84      * @throws IllegalArgumentException if the field name is blank or empty or is matched at multiple places
85      * in the inheritance hierarchy
86      */
getField(final Class<?> cls, final String fieldName, final boolean forceAccess)87     public static Field getField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
88         Objects.requireNonNull(cls, "cls");
89         Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
90         // FIXME is this workaround still needed? lang requires Java 6
91         // Sun Java 1.3 has a bugged implementation of getField hence we write the
92         // code ourselves
93 
94         // getField() will return the Field object with the declaring class
95         // set correctly to the class that declares the field. Thus requesting the
96         // field on a subclass will return the field from the superclass.
97         //
98         // priority order for lookup:
99         // searchclass private/protected/package/public
100         // superclass protected/package/public
101         // private/different package blocks access to further superclasses
102         // implementedinterface public
103 
104         // check up the superclass hierarchy
105         for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
106             try {
107                 final Field field = acls.getDeclaredField(fieldName);
108                 // getDeclaredField checks for non-public scopes as well
109                 // and it returns accurate results
110                 if (!MemberUtils.isPublic(field)) {
111                     if (!forceAccess) {
112                         continue;
113                     }
114                     field.setAccessible(true);
115                 }
116                 return field;
117             } catch (final NoSuchFieldException ignored) {
118                 // ignore
119             }
120         }
121         // check the public interface case. This must be manually searched for
122         // incase there is a public supersuperclass field hidden by a private/package
123         // superclass field.
124         Field match = null;
125         for (final Class<?> class1 : ClassUtils.getAllInterfaces(cls)) {
126             try {
127                 final Field test = class1.getField(fieldName);
128                 Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s"
129                         + "; a matching field exists on two or more implemented interfaces.", fieldName, cls);
130                 match = test;
131             } catch (final NoSuchFieldException ignored) {
132                 // ignore
133             }
134         }
135         return match;
136     }
137 
138     /**
139      * Gets an accessible {@link Field} by name respecting scope. Only the specified class will be considered.
140      *
141      * @param cls
142      *            the {@link Class} to reflect, must not be {@code null}
143      * @param fieldName
144      *            the field name to obtain
145      * @return the Field object
146      * @throws IllegalArgumentException
147      *             if the class is {@code null}, or the field name is blank or empty
148      */
getDeclaredField(final Class<?> cls, final String fieldName)149     public static Field getDeclaredField(final Class<?> cls, final String fieldName) {
150         return getDeclaredField(cls, fieldName, false);
151     }
152 
153     /**
154      * Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be
155      * considered.
156      *
157      * @param cls
158      *            the {@link Class} to reflect, must not be {@code null}
159      * @param fieldName
160      *            the field name to obtain
161      * @param forceAccess
162      *            whether to break scope restrictions using the
163      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
164      *            match {@code public} fields.
165      * @return the Field object
166      * @throws IllegalArgumentException
167      *             if the class is {@code null}, or the field name is blank or empty
168      */
getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess)169     public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
170         Objects.requireNonNull(cls, "cls");
171         Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
172         try {
173             // only consider the specified class by using getDeclaredField()
174             final Field field = cls.getDeclaredField(fieldName);
175             if (!MemberUtils.isAccessible(field)) {
176                 if (!forceAccess) {
177                     return null;
178                 }
179                 field.setAccessible(true);
180             }
181             return field;
182         } catch (final NoSuchFieldException ignored) {
183             // ignore
184         }
185         return null;
186     }
187 
188     /**
189      * Gets all fields of the given class and its parents (if any).
190      *
191      * @param cls
192      *            the {@link Class} to query
193      * @return an array of Fields (possibly empty).
194      * @throws IllegalArgumentException
195      *             if the class is {@code null}
196      * @since 3.2
197      */
getAllFields(final Class<?> cls)198     public static Field[] getAllFields(final Class<?> cls) {
199         return getAllFieldsList(cls).toArray(ArrayUtils.EMPTY_FIELD_ARRAY);
200     }
201 
202     /**
203      * Gets all fields of the given class and its parents (if any).
204      *
205      * @param cls
206      *            the {@link Class} to query
207      * @return a list of Fields (possibly empty).
208      * @throws IllegalArgumentException
209      *             if the class is {@code null}
210      * @since 3.2
211      */
getAllFieldsList(final Class<?> cls)212     public static List<Field> getAllFieldsList(final Class<?> cls) {
213         Objects.requireNonNull(cls, "cls");
214         final List<Field> allFields = new ArrayList<>();
215         Class<?> currentClass = cls;
216         while (currentClass != null) {
217             final Field[] declaredFields = currentClass.getDeclaredFields();
218             Collections.addAll(allFields, declaredFields);
219             currentClass = currentClass.getSuperclass();
220         }
221         return allFields;
222     }
223 
224     /**
225      * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
226      * @param cls
227      *            the {@link Class} to query
228      * @param annotationCls
229      *            the {@link Annotation} that must be present on a field to be matched
230      * @return an array of Fields (possibly empty).
231      * @throws IllegalArgumentException
232      *            if the class or annotation are {@code null}
233      * @since 3.4
234      */
getFieldsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls)235     public static Field[] getFieldsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
236         return getFieldsListWithAnnotation(cls, annotationCls).toArray(ArrayUtils.EMPTY_FIELD_ARRAY);
237     }
238 
239     /**
240      * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
241      * @param cls
242      *            the {@link Class} to query
243      * @param annotationCls
244      *            the {@link Annotation} that must be present on a field to be matched
245      * @return a list of Fields (possibly empty).
246      * @throws IllegalArgumentException
247      *            if the class or annotation are {@code null}
248      * @since 3.4
249      */
getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls)250     public static List<Field> getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
251         Objects.requireNonNull(annotationCls, "annotationCls");
252         return getAllFieldsList(cls).stream().filter(field -> field.getAnnotation(annotationCls) != null).collect(Collectors.toList());
253     }
254 
255     /**
256      * Reads an accessible {@code static} {@link Field}.
257      *
258      * @param field
259      *            to read
260      * @return the field value
261      * @throws IllegalArgumentException
262      *             if the field is {@code null}, or not {@code static}
263      * @throws IllegalAccessException
264      *             if the field is not accessible
265      */
readStaticField(final Field field)266     public static Object readStaticField(final Field field) throws IllegalAccessException {
267         return readStaticField(field, false);
268     }
269 
270     /**
271      * Reads a static {@link Field}.
272      *
273      * @param field
274      *            to read
275      * @param forceAccess
276      *            whether to break scope restrictions using the
277      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
278      * @return the field value
279      * @throws IllegalArgumentException
280      *             if the field is {@code null} or not {@code static}
281      * @throws IllegalAccessException
282      *             if the field is not made accessible
283      */
readStaticField(final Field field, final boolean forceAccess)284     public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
285         Objects.requireNonNull(field, "field");
286         Validate.isTrue(MemberUtils.isStatic(field), "The field '%s' is not static", field.getName());
287         return readField(field, (Object) null, forceAccess);
288     }
289 
290     /**
291      * Reads the named {@code public static} {@link Field}. Superclasses will be considered.
292      *
293      * @param cls
294      *            the {@link Class} to reflect, must not be {@code null}
295      * @param fieldName
296      *            the field name to obtain
297      * @return the value of the field
298      * @throws IllegalArgumentException
299      *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
300      *             not be found
301      * @throws IllegalAccessException
302      *             if the field is not accessible
303      */
readStaticField(final Class<?> cls, final String fieldName)304     public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
305         return readStaticField(cls, fieldName, false);
306     }
307 
308     /**
309      * Reads the named {@code static} {@link Field}. Superclasses will be considered.
310      *
311      * @param cls
312      *            the {@link Class} to reflect, must not be {@code null}
313      * @param fieldName
314      *            the field name to obtain
315      * @param forceAccess
316      *            whether to break scope restrictions using the
317      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
318      *            match {@code public} fields.
319      * @return the Field object
320      * @throws IllegalArgumentException
321      *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
322      *             not be found
323      * @throws IllegalAccessException
324      *             if the field is not made accessible
325      */
readStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess)326     public static Object readStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
327         final Field field = getField(cls, fieldName, forceAccess);
328         Validate.notNull(field, "Cannot locate field '%s' on %s", fieldName, cls);
329         // already forced access above, don't repeat it here:
330         return readStaticField(field, false);
331     }
332 
333     /**
334      * Gets the value of a {@code static} {@link Field} by name. The field must be {@code public}. Only the specified
335      * class will be considered.
336      *
337      * @param cls
338      *            the {@link Class} to reflect, must not be {@code null}
339      * @param fieldName
340      *            the field name to obtain
341      * @return the value of the field
342      * @throws IllegalArgumentException
343      *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
344      *             not be found
345      * @throws IllegalAccessException
346      *             if the field is not accessible
347      */
readDeclaredStaticField(final Class<?> cls, final String fieldName)348     public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
349         return readDeclaredStaticField(cls, fieldName, false);
350     }
351 
352     /**
353      * Gets the value of a {@code static} {@link Field} by name. Only the specified class will be considered.
354      *
355      * @param cls
356      *            the {@link Class} to reflect, must not be {@code null}
357      * @param fieldName
358      *            the field name to obtain
359      * @param forceAccess
360      *            whether to break scope restrictions using the
361      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
362      *            match {@code public} fields.
363      * @return the Field object
364      * @throws IllegalArgumentException
365      *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
366      *             not be found
367      * @throws IllegalAccessException
368      *             if the field is not made accessible
369      */
readDeclaredStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess)370     public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
371         final Field field = getDeclaredField(cls, fieldName, forceAccess);
372         Validate.notNull(field, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
373         // already forced access above, don't repeat it here:
374         return readStaticField(field, false);
375     }
376 
377     /**
378      * Reads an accessible {@link Field}.
379      *
380      * @param field
381      *            the field to use
382      * @param target
383      *            the object to call on, may be {@code null} for {@code static} fields
384      * @return the field value
385      * @throws IllegalArgumentException
386      *             if the field is {@code null}
387      * @throws IllegalAccessException
388      *             if the field is not accessible
389      */
readField(final Field field, final Object target)390     public static Object readField(final Field field, final Object target) throws IllegalAccessException {
391         return readField(field, target, false);
392     }
393 
394     /**
395      * Reads a {@link Field}.
396      *
397      * @param field
398      *            the field to use
399      * @param target
400      *            the object to call on, may be {@code null} for {@code static} fields
401      * @param forceAccess
402      *            whether to break scope restrictions using the
403      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
404      * @return the field value
405      * @throws IllegalArgumentException
406      *             if the field is {@code null}
407      * @throws IllegalAccessException
408      *             if the field is not made accessible
409      */
readField(final Field field, final Object target, final boolean forceAccess)410     public static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException {
411         Objects.requireNonNull(field, "field");
412         if (forceAccess && !field.isAccessible()) {
413             field.setAccessible(true);
414         } else {
415             MemberUtils.setAccessibleWorkaround(field);
416         }
417         return field.get(target);
418     }
419 
420     /**
421      * Reads the named {@code public} {@link Field}. Superclasses will be considered.
422      *
423      * @param target
424      *            the object to reflect, must not be {@code null}
425      * @param fieldName
426      *            the field name to obtain
427      * @return the value of the field
428      * @throws IllegalArgumentException
429      *             if the class is {@code null}, or the field name is blank or empty or could not be found
430      * @throws IllegalAccessException
431      *             if the named field is not {@code public}
432      */
readField(final Object target, final String fieldName)433     public static Object readField(final Object target, final String fieldName) throws IllegalAccessException {
434         return readField(target, fieldName, false);
435     }
436 
437     /**
438      * Reads the named {@link Field}. Superclasses will be considered.
439      *
440      * @param target
441      *            the object to reflect, must not be {@code null}
442      * @param fieldName
443      *            the field name to obtain
444      * @param forceAccess
445      *            whether to break scope restrictions using the
446      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
447      *            match {@code public} fields.
448      * @return the field value
449      * @throws IllegalArgumentException
450      *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
451      * @throws IllegalAccessException
452      *             if the named field is not made accessible
453      */
readField(final Object target, final String fieldName, final boolean forceAccess)454     public static Object readField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
455         Objects.requireNonNull(target, "target");
456         final Class<?> cls = target.getClass();
457         final Field field = getField(cls, fieldName, forceAccess);
458         Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
459         // already forced access above, don't repeat it here:
460         return readField(field, target, false);
461     }
462 
463     /**
464      * Reads the named {@code public} {@link Field}. Only the class of the specified object will be considered.
465      *
466      * @param target
467      *            the object to reflect, must not be {@code null}
468      * @param fieldName
469      *            the field name to obtain
470      * @return the value of the field
471      * @throws IllegalArgumentException
472      *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
473      * @throws IllegalAccessException
474      *             if the named field is not {@code public}
475      */
readDeclaredField(final Object target, final String fieldName)476     public static Object readDeclaredField(final Object target, final String fieldName) throws IllegalAccessException {
477         return readDeclaredField(target, fieldName, false);
478     }
479 
480     /**
481      * Gets a {@link Field} value by name. Only the class of the specified object will be considered.
482      *
483      * @param target
484      *            the object to reflect, must not be {@code null}
485      * @param fieldName
486      *            the field name to obtain
487      * @param forceAccess
488      *            whether to break scope restrictions using the
489      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
490      *            match public fields.
491      * @return the Field object
492      * @throws IllegalArgumentException
493      *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
494      * @throws IllegalAccessException
495      *             if the field is not made accessible
496      */
readDeclaredField(final Object target, final String fieldName, final boolean forceAccess)497     public static Object readDeclaredField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
498         Objects.requireNonNull(target, "target");
499         final Class<?> cls = target.getClass();
500         final Field field = getDeclaredField(cls, fieldName, forceAccess);
501         Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls, fieldName);
502         // already forced access above, don't repeat it here:
503         return readField(field, target, false);
504     }
505 
506     /**
507      * Writes a {@code public static} {@link Field}.
508      *
509      * @param field
510      *            to write
511      * @param value
512      *            to set
513      * @throws IllegalArgumentException
514      *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
515      * @throws IllegalAccessException
516      *             if the field is not {@code public} or is {@code final}
517      */
writeStaticField(final Field field, final Object value)518     public static void writeStaticField(final Field field, final Object value) throws IllegalAccessException {
519         writeStaticField(field, value, false);
520     }
521 
522     /**
523      * Writes a static {@link Field}.
524      *
525      * @param field
526      *            to write
527      * @param value
528      *            to set
529      * @param forceAccess
530      *            whether to break scope restrictions using the
531      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
532      *            match {@code public} fields.
533      * @throws IllegalArgumentException
534      *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
535      * @throws IllegalAccessException
536      *             if the field is not made accessible or is {@code final}
537      */
writeStaticField(final Field field, final Object value, final boolean forceAccess)538     public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
539         Objects.requireNonNull(field, "field");
540         Validate.isTrue(MemberUtils.isStatic(field), "The field %s.%s is not static", field.getDeclaringClass().getName(),
541                 field.getName());
542         writeField(field, (Object) null, value, forceAccess);
543     }
544 
545     /**
546      * Writes a named {@code public static} {@link Field}. Superclasses will be considered.
547      *
548      * @param cls
549      *            {@link Class} on which the field is to be found
550      * @param fieldName
551      *            to write
552      * @param value
553      *            to set
554      * @throws IllegalArgumentException
555      *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
556      *             not {@code static}, or {@code value} is not assignable
557      * @throws IllegalAccessException
558      *             if the field is not {@code public} or is {@code final}
559      */
writeStaticField(final Class<?> cls, final String fieldName, final Object value)560     public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
561         writeStaticField(cls, fieldName, value, false);
562     }
563 
564     /**
565      * Writes a named {@code static} {@link Field}. Superclasses will be considered.
566      *
567      * @param cls
568      *            {@link Class} on which the field is to be found
569      * @param fieldName
570      *            to write
571      * @param value
572      *            to set
573      * @param forceAccess
574      *            whether to break scope restrictions using the
575      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
576      *            match {@code public} fields.
577      * @throws IllegalArgumentException
578      *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
579      *             not {@code static}, or {@code value} is not assignable
580      * @throws IllegalAccessException
581      *             if the field is not made accessible or is {@code final}
582      */
writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)583     public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
584             throws IllegalAccessException {
585         final Field field = getField(cls, fieldName, forceAccess);
586         Validate.notNull(field, "Cannot locate field %s on %s", fieldName, cls);
587         // already forced access above, don't repeat it here:
588         writeStaticField(field, value, false);
589     }
590 
591     /**
592      * Writes a named {@code public static} {@link Field}. Only the specified class will be considered.
593      *
594      * @param cls
595      *            {@link Class} on which the field is to be found
596      * @param fieldName
597      *            to write
598      * @param value
599      *            to set
600      * @throws IllegalArgumentException
601      *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
602      *             not {@code static}, or {@code value} is not assignable
603      * @throws IllegalAccessException
604      *             if the field is not {@code public} or is {@code final}
605      */
writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value)606     public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
607         writeDeclaredStaticField(cls, fieldName, value, false);
608     }
609 
610     /**
611      * Writes a named {@code static} {@link Field}. Only the specified class will be considered.
612      *
613      * @param cls
614      *            {@link Class} on which the field is to be found
615      * @param fieldName
616      *            to write
617      * @param value
618      *            to set
619      * @param forceAccess
620      *            whether to break scope restrictions using the {@code AccessibleObject#setAccessible(boolean)} method.
621      *            {@code false} will only match {@code public} fields.
622      * @throws IllegalArgumentException
623      *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
624      *             not {@code static}, or {@code value} is not assignable
625      * @throws IllegalAccessException
626      *             if the field is not made accessible or is {@code final}
627      */
writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)628     public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
629             throws IllegalAccessException {
630         final Field field = getDeclaredField(cls, fieldName, forceAccess);
631         Validate.notNull(field, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
632         // already forced access above, don't repeat it here:
633         writeField(field, (Object) null, value, false);
634     }
635 
636     /**
637      * Writes an accessible {@link Field}.
638      *
639      * @param field
640      *            to write
641      * @param target
642      *            the object to call on, may be {@code null} for {@code static} fields
643      * @param value
644      *            to set
645      * @throws IllegalAccessException
646      *             if the field or target is {@code null}, the field is not accessible or is {@code final}, or
647      *             {@code value} is not assignable
648      */
writeField(final Field field, final Object target, final Object value)649     public static void writeField(final Field field, final Object target, final Object value) throws IllegalAccessException {
650         writeField(field, target, value, false);
651     }
652 
653     /**
654      * Writes a {@link Field}.
655      *
656      * @param field
657      *            to write
658      * @param target
659      *            the object to call on, may be {@code null} for {@code static} fields
660      * @param value
661      *            to set
662      * @param forceAccess
663      *            whether to break scope restrictions using the
664      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
665      *            match {@code public} fields.
666      * @throws IllegalArgumentException
667      *             if the field is {@code null} or {@code value} is not assignable
668      * @throws IllegalAccessException
669      *             if the field is not made accessible or is {@code final}
670      */
writeField(final Field field, final Object target, final Object value, final boolean forceAccess)671     public static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess)
672             throws IllegalAccessException {
673         Objects.requireNonNull(field, "field");
674         if (forceAccess && !field.isAccessible()) {
675             field.setAccessible(true);
676         } else {
677             MemberUtils.setAccessibleWorkaround(field);
678         }
679         field.set(target, value);
680     }
681 
682     /**
683      * Removes the final modifier from a {@link Field}.
684      *
685      * @param field
686      *            to remove the final modifier
687      * @throws IllegalArgumentException
688      *             if the field is {@code null}
689      * @since 3.2
690      */
removeFinalModifier(final Field field)691     public static void removeFinalModifier(final Field field) {
692         removeFinalModifier(field, true);
693     }
694 
695     /**
696      * Removes the final modifier from a {@link Field}.
697      *
698      * @param field
699      *            to remove the final modifier
700      * @param forceAccess
701      *            whether to break scope restrictions using the
702      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
703      *            match {@code public} fields.
704      * @throws IllegalArgumentException
705      *             if the field is {@code null}
706      * @deprecated As of Java 12, we can no longer drop the {@code final} modifier, thus
707      *             rendering this method obsolete. The JDK discussion about this change can be found
708      *             here: https://mail.openjdk.java.net/pipermail/core-libs-dev/2018-November/056486.html
709      * @since 3.3
710      */
711     @Deprecated
removeFinalModifier(final Field field, final boolean forceAccess)712     public static void removeFinalModifier(final Field field, final boolean forceAccess) {
713         Objects.requireNonNull(field, "field");
714 
715         try {
716             if (Modifier.isFinal(field.getModifiers())) {
717                 // Do all JREs implement Field with a private ivar called "modifiers"?
718                 final Field modifiersField = Field.class.getDeclaredField("modifiers");
719                 final boolean doForceAccess = forceAccess && !modifiersField.isAccessible();
720                 if (doForceAccess) {
721                     modifiersField.setAccessible(true);
722                 }
723                 try {
724                     modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
725                 } finally {
726                     if (doForceAccess) {
727                         modifiersField.setAccessible(false);
728                     }
729                 }
730             }
731         } catch (final NoSuchFieldException | IllegalAccessException e) {
732             if (SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_12)) {
733               throw new UnsupportedOperationException(
734                   "In java 12+ final cannot be removed.",
735                   e
736               );
737             }
738             // else no exception is thrown because we can modify final.
739         }
740     }
741 
742     /**
743      * Writes a {@code public} {@link Field}. Superclasses will be considered.
744      *
745      * @param target
746      *            the object to reflect, must not be {@code null}
747      * @param fieldName
748      *            the field name to obtain
749      * @param value
750      *            to set
751      * @throws IllegalArgumentException
752      *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
753      *             {@code value} is not assignable
754      * @throws IllegalAccessException
755      *             if the field is not accessible
756      */
writeField(final Object target, final String fieldName, final Object value)757     public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
758         writeField(target, fieldName, value, false);
759     }
760 
761     /**
762      * Writes a {@link Field}. Superclasses will be considered.
763      *
764      * @param target
765      *            the object to reflect, must not be {@code null}
766      * @param fieldName
767      *            the field name to obtain
768      * @param value
769      *            to set
770      * @param forceAccess
771      *            whether to break scope restrictions using the
772      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
773      *            match {@code public} fields.
774      * @throws IllegalArgumentException
775      *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
776      *             {@code value} is not assignable
777      * @throws IllegalAccessException
778      *             if the field is not made accessible
779      */
writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess)780     public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
781             throws IllegalAccessException {
782         Objects.requireNonNull(target, "target");
783         final Class<?> cls = target.getClass();
784         final Field field = getField(cls, fieldName, forceAccess);
785         Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
786         // already forced access above, don't repeat it here:
787         writeField(field, target, value, false);
788     }
789 
790     /**
791      * Writes a {@code public} {@link Field}. Only the specified class will be considered.
792      *
793      * @param target
794      *            the object to reflect, must not be {@code null}
795      * @param fieldName
796      *            the field name to obtain
797      * @param value
798      *            to set
799      * @throws IllegalArgumentException
800      *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
801      *             {@code value} is not assignable
802      * @throws IllegalAccessException
803      *             if the field is not made accessible
804      */
writeDeclaredField(final Object target, final String fieldName, final Object value)805     public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
806         writeDeclaredField(target, fieldName, value, false);
807     }
808 
809     /**
810      * Writes a {@code public} {@link Field}. Only the specified class will be considered.
811      *
812      * @param target
813      *            the object to reflect, must not be {@code null}
814      * @param fieldName
815      *            the field name to obtain
816      * @param value
817      *            to set
818      * @param forceAccess
819      *            whether to break scope restrictions using the
820      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
821      *            match {@code public} fields.
822      * @throws IllegalArgumentException
823      *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
824      *             {@code value} is not assignable
825      * @throws IllegalAccessException
826      *             if the field is not made accessible
827      */
writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)828     public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
829             throws IllegalAccessException {
830         Objects.requireNonNull(target, "target");
831         final Class<?> cls = target.getClass();
832         final Field field = getDeclaredField(cls, fieldName, forceAccess);
833         Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
834         // already forced access above, don't repeat it here:
835         writeField(field, target, value, false);
836     }
837 }
838