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