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