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