    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.lang3.reflect;
    018
    019import java.lang.annotation.Annotation;
    020import java.lang.reflect.Field;
    021import java.lang.reflect.Modifier;
    022import java.util.ArrayList;
    023import java.util.Collections;
    024import java.util.List;
    025
    026import org.apache.commons.lang3.ArrayUtils;
    027import org.apache.commons.lang3.ClassUtils;
    028import org.apache.commons.lang3.JavaVersion;
    029import org.apache.commons.lang3.StringUtils;
    030import org.apache.commons.lang3.SystemUtils;
    031import org.apache.commons.lang3.Validate;
    032
    033/**
    034 * Utilities for working with {@link Field}s by reflection. Adapted and refactored from the dormant [reflect] Commons
    035 * sandbox component.
    036 * <p>
    037 * The ability is provided to break the scoping restrictions coded by the programmer. This can allow fields to be
    038 * changed that shouldn't be. This facility should be used with care.
    039 *
    040 * @since 2.5
    041 */
    042public class FieldUtils {
    043
    044    /**
    045     * {@link FieldUtils} instances should NOT be constructed in standard programming.
    046     * <p>
    047     * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
    048     * </p>
    049     */
    050    public FieldUtils() {
    051        super();
    052    }
    053
    054    /**
    055     * Gets an accessible {@link Field} by name respecting scope. Superclasses/interfaces will be considered.
    056     *
    057     * @param cls
    058     *            the {@link Class} to reflect, must not be {@code null}
    059     * @param fieldName
    060     *            the field name to obtain
    061     * @return the Field object
    062     * @throws IllegalArgumentException
    063     *             if the class is {@code null}, or the field name is blank or empty
    064     */
    065    public static Field getField(final Class<?> cls, final String fieldName) {
    066        final Field field = getField(cls, fieldName, false);
    067        MemberUtils.setAccessibleWorkaround(field);
    068        return field;
    069    }
    070
    071    /**
    072     * Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be
    073     * considered.
    074     *
    075     * @param cls
    076     *            the {@link Class} to reflect, must not be {@code null}
    077     * @param fieldName
    078     *            the field name to obtain
    079     * @param forceAccess
    080     *            whether to break scope restrictions using the
    081     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    082     *            match {@code public} fields.
    083     * @return the Field object
    084     * @throws IllegalArgumentException
    085     *             if the class is {@code null}, or the field name is blank or empty or is matched at multiple places
    086     *             in the inheritance hierarchy
    087     */
    088    public static Field getField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
    089        Validate.notNull(cls, "The class must not be null");
    090        Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
    091        // FIXME is this workaround still needed? lang requires Java 6
    092        // Sun Java 1.3 has a bugged implementation of getField hence we write the
    093        // code ourselves
    094
    095        // getField() will return the Field object with the declaring class
    096        // set correctly to the class that declares the field. Thus requesting the
    097        // field on a subclass will return the field from the superclass.
    098        //
    099        // priority order for lookup:
    100        // searchclass private/protected/package/public
    101        // superclass protected/package/public
    102        // private/different package blocks access to further superclasses
    103        // implementedinterface public
    104
    105        // check up the superclass hierarchy
    106        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
    107            try {
    108                final Field field = acls.getDeclaredField(fieldName);
    109                // getDeclaredField checks for non-public scopes as well
    110                // and it returns accurate results
    111                if (!Modifier.isPublic(field.getModifiers())) {
    112                    if (forceAccess) {
    113                        field.setAccessible(true);
    114                    } else {
    115                        continue;
    116                    }
    117                }
    118                return field;
    119            } catch (final NoSuchFieldException ex) { // NOPMD
    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 ex) { // NOPMD
    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 IllegalArgumentException
    149     *             if the class is {@code null}, or the field name is blank or empty
    150     */
    151    public static Field getDeclaredField(final Class<?> cls, final String fieldName) {
    152        return getDeclaredField(cls, fieldName, false);
    153    }
    154
    155    /**
    156     * Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be
    157     * considered.
    158     *
    159     * @param cls
    160     *            the {@link Class} to reflect, must not be {@code null}
    161     * @param fieldName
    162     *            the field name to obtain
    163     * @param forceAccess
    164     *            whether to break scope restrictions using the
    165     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    166     *            match {@code public} fields.
    167     * @return the Field object
    168     * @throws IllegalArgumentException
    169     *             if the class is {@code null}, or the field name is blank or empty
    170     */
    171    public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
    172        Validate.notNull(cls, "The class must not be null");
    173        Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
    174        try {
    175            // only consider the specified class by using getDeclaredField()
    176            final Field field = cls.getDeclaredField(fieldName);
    177            if (!MemberUtils.isAccessible(field)) {
    178                if (forceAccess) {
    179                    field.setAccessible(true);
    180                } else {
    181                    return null;
    182                }
    183            }
    184            return field;
    185        } catch (final NoSuchFieldException e) { // NOPMD
    186            // ignore
    187        }
    188        return null;
    189    }
    190
    191    /**
    192     * Gets all fields of the given class and its parents (if any).
    193     *
    194     * @param cls
    195     *            the {@link Class} to query
    196     * @return an array of Fields (possibly empty).
    197     * @throws IllegalArgumentException
    198     *             if the class is {@code null}
    199     * @since 3.2
    200     */
    201    public static Field[] getAllFields(final Class<?> cls) {
    202        final List<Field> allFieldsList = getAllFieldsList(cls);
    203        return allFieldsList.toArray(ArrayUtils.EMPTY_FIELD_ARRAY);
    204    }
    205
    206    /**
    207     * Gets all fields of the given class and its parents (if any).
    208     *
    209     * @param cls
    210     *            the {@link Class} to query
    211     * @return an array of Fields (possibly empty).
    212     * @throws IllegalArgumentException
    213     *             if the class is {@code null}
    214     * @since 3.2
    215     */
    216    public static List<Field> getAllFieldsList(final Class<?> cls) {
    217        Validate.notNull(cls, "The class must not be null");
    218        final List<Field> allFields = new ArrayList<>();
    219        Class<?> currentClass = cls;
    220        while (currentClass != null) {
    221            final Field[] declaredFields = currentClass.getDeclaredFields();
    222            Collections.addAll(allFields, declaredFields);
    223            currentClass = currentClass.getSuperclass();
    224        }
    225        return allFields;
    226    }
    227
    228    /**
    229     * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
    230     * @param cls
    231     *            the {@link Class} to query
    232     * @param annotationCls
    233     *            the {@link Annotation} that must be present on a field to be matched
    234     * @return an array of Fields (possibly empty).
    235     * @throws IllegalArgumentException
    236     *            if the class or annotation are {@code null}
    237     * @since 3.4
    238     */
    239    public static Field[] getFieldsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
    240        final List<Field> annotatedFieldsList = getFieldsListWithAnnotation(cls, annotationCls);
    241        return annotatedFieldsList.toArray(ArrayUtils.EMPTY_FIELD_ARRAY);
    242    }
    243
    244    /**
    245     * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
    246     * @param cls
    247     *            the {@link Class} to query
    248     * @param annotationCls
    249     *            the {@link Annotation} that must be present on a field to be matched
    250     * @return a list of Fields (possibly empty).
    251     * @throws IllegalArgumentException
    252     *            if the class or annotation are {@code null}
    253     * @since 3.4
    254     */
    255    public static List<Field> getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
    256        Validate.notNull(annotationCls, "The annotation class must not be null");
    257        final List<Field> allFields = getAllFieldsList(cls);
    258        final List<Field> annotatedFields = new ArrayList<>();
    259        for (final Field field : allFields) {
    260            if (field.getAnnotation(annotationCls) != null) {
    261                annotatedFields.add(field);
    262            }
    263        }
    264        return annotatedFields;
    265    }
    266
    267    /**
    268     * Reads an accessible {@code static} {@link Field}.
    269     *
    270     * @param field
    271     *            to read
    272     * @return the field value
    273     * @throws IllegalArgumentException
    274     *             if the field is {@code null}, or not {@code static}
    275     * @throws IllegalAccessException
    276     *             if the field is not accessible
    277     */
    278    public static Object readStaticField(final Field field) throws IllegalAccessException {
    279        return readStaticField(field, false);
    280    }
    281
    282    /**
    283     * Reads a static {@link Field}.
    284     *
    285     * @param field
    286     *            to read
    287     * @param forceAccess
    288     *            whether to break scope restrictions using the
    289     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
    290     * @return the field value
    291     * @throws IllegalArgumentException
    292     *             if the field is {@code null} or not {@code static}
    293     * @throws IllegalAccessException
    294     *             if the field is not made accessible
    295     */
    296    public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
    297        Validate.notNull(field, "The field must not be null");
    298        Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static", field.getName());
    299        return readField(field, (Object) null, forceAccess);
    300    }
    301
    302    /**
    303     * Reads the named {@code public static} {@link Field}. Superclasses will be considered.
    304     *
    305     * @param cls
    306     *            the {@link Class} to reflect, must not be {@code null}
    307     * @param fieldName
    308     *            the field name to obtain
    309     * @return the value of the field
    310     * @throws IllegalArgumentException
    311     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
    312     *             not be found
    313     * @throws IllegalAccessException
    314     *             if the field is not accessible
    315     */
    316    public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
    317        return readStaticField(cls, fieldName, false);
    318    }
    319
    320    /**
    321     * Reads the named {@code static} {@link Field}. Superclasses will be considered.
    322     *
    323     * @param cls
    324     *            the {@link Class} to reflect, must not be {@code null}
    325     * @param fieldName
    326     *            the field name to obtain
    327     * @param forceAccess
    328     *            whether to break scope restrictions using the
    329     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    330     *            match {@code public} fields.
    331     * @return the Field object
    332     * @throws IllegalArgumentException
    333     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
    334     *             not be found
    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 IllegalArgumentException
    355     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
    356     *             not be found
    357     * @throws IllegalAccessException
    358     *             if the field is not accessible
    359     */
    360    public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
    361        return readDeclaredStaticField(cls, fieldName, false);
    362    }
    363
    364    /**
    365     * Gets the value of a {@code static} {@link Field} by name. Only the specified class will be considered.
    366     *
    367     * @param cls
    368     *            the {@link Class} to reflect, must not be {@code null}
    369     * @param fieldName
    370     *            the field name to obtain
    371     * @param forceAccess
    372     *            whether to break scope restrictions using the
    373     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    374     *            match {@code public} fields.
    375     * @return the Field object
    376     * @throws IllegalArgumentException
    377     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
    378     *             not be found
    379     * @throws IllegalAccessException
    380     *             if the field is not made accessible
    381     */
    382    public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
    383        final Field field = getDeclaredField(cls, fieldName, forceAccess);
    384        Validate.notNull(field, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
    385        // already forced access above, don't repeat it here:
    386        return readStaticField(field, false);
    387    }
    388
    389    /**
    390     * Reads an accessible {@link Field}.
    391     *
    392     * @param field
    393     *            the field to use
    394     * @param target
    395     *            the object to call on, may be {@code null} for {@code static} fields
    396     * @return the field value
    397     * @throws IllegalArgumentException
    398     *             if the field is {@code null}
    399     * @throws IllegalAccessException
    400     *             if the field is not accessible
    401     */
    402    public static Object readField(final Field field, final Object target) throws IllegalAccessException {
    403        return readField(field, target, false);
    404    }
    405
    406    /**
    407     * Reads a {@link Field}.
    408     *
    409     * @param field
    410     *            the field to use
    411     * @param target
    412     *            the object to call on, may be {@code null} for {@code static} fields
    413     * @param forceAccess
    414     *            whether to break scope restrictions using the
    415     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
    416     * @return the field value
    417     * @throws IllegalArgumentException
    418     *             if the field is {@code null}
    419     * @throws IllegalAccessException
    420     *             if the field is not made accessible
    421     */
    422    public static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException {
    423        Validate.notNull(field, "The field must not be null");
    424        if (forceAccess && !field.isAccessible()) {
    425            field.setAccessible(true);
    426        } else {
    427            MemberUtils.setAccessibleWorkaround(field);
    428        }
    429        return field.get(target);
    430    }
    431
    432    /**
    433     * Reads the named {@code public} {@link Field}. Superclasses will be considered.
    434     *
    435     * @param target
    436     *            the object to reflect, must not be {@code null}
    437     * @param fieldName
    438     *            the field name to obtain
    439     * @return the value of the field
    440     * @throws IllegalArgumentException
    441     *             if the class is {@code null}, or the field name is blank or empty or could not be found
    442     * @throws IllegalAccessException
    443     *             if the named field is not {@code public}
    444     */
    445    public static Object readField(final Object target, final String fieldName) throws IllegalAccessException {
    446        return readField(target, fieldName, false);
    447    }
    448
    449    /**
    450     * Reads the named {@link Field}. Superclasses will be considered.
    451     *
    452     * @param target
    453     *            the object to reflect, must not be {@code null}
    454     * @param fieldName
    455     *            the field name to obtain
    456     * @param forceAccess
    457     *            whether to break scope restrictions using the
    458     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    459     *            match {@code public} fields.
    460     * @return the field value
    461     * @throws IllegalArgumentException
    462     *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
    463     * @throws IllegalAccessException
    464     *             if the named field is not made accessible
    465     */
    466    public static Object readField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
    467        Validate.notNull(target, "target object must not be null");
    468        final Class<?> cls = target.getClass();
    469        final Field field = getField(cls, fieldName, forceAccess);
    470        Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
    471        // already forced access above, don't repeat it here:
    472        return readField(field, target, false);
    473    }
    474
    475    /**
    476     * Reads the named {@code public} {@link Field}. Only the class of the specified object will be considered.
    477     *
    478     * @param target
    479     *            the object to reflect, must not be {@code null}
    480     * @param fieldName
    481     *            the field name to obtain
    482     * @return the value of the field
    483     * @throws IllegalArgumentException
    484     *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
    485     * @throws IllegalAccessException
    486     *             if the named field is not {@code public}
    487     */
    488    public static Object readDeclaredField(final Object target, final String fieldName) throws IllegalAccessException {
    489        return readDeclaredField(target, fieldName, false);
    490    }
    491
    492    /**
    493     * Gets a {@link Field} value by name. Only the class of the specified object will be considered.
    494     *
    495     * @param target
    496     *            the object to reflect, must not be {@code null}
    497     * @param fieldName
    498     *            the field name to obtain
    499     * @param forceAccess
    500     *            whether to break scope restrictions using the
    501     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    502     *            match public fields.
    503     * @return the Field object
    504     * @throws IllegalArgumentException
    505     *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
    506     * @throws IllegalAccessException
    507     *             if the field is not made accessible
    508     */
    509    public static Object readDeclaredField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
    510        Validate.notNull(target, "target object must not be null");
    511        final Class<?> cls = target.getClass();
    512        final Field field = getDeclaredField(cls, fieldName, forceAccess);
    513        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls, fieldName);
    514        // already forced access above, don't repeat it here:
    515        return readField(field, target, false);
    516    }
    517
    518    /**
    519     * Writes a {@code public static} {@link Field}.
    520     *
    521     * @param field
    522     *            to write
    523     * @param value
    524     *            to set
    525     * @throws IllegalArgumentException
    526     *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
    527     * @throws IllegalAccessException
    528     *             if the field is not {@code public} or is {@code final}
    529     */
    530    public static void writeStaticField(final Field field, final Object value) throws IllegalAccessException {
    531        writeStaticField(field, value, false);
    532    }
    533
    534    /**
    535     * Writes a static {@link Field}.
    536     *
    537     * @param field
    538     *            to write
    539     * @param value
    540     *            to set
    541     * @param forceAccess
    542     *            whether to break scope restrictions using the
    543     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    544     *            match {@code public} fields.
    545     * @throws IllegalArgumentException
    546     *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
    547     * @throws IllegalAccessException
    548     *             if the field is not made accessible or is {@code final}
    549     */
    550    public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
    551        Validate.notNull(field, "The field must not be null");
    552        Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static", field.getDeclaringClass().getName(),
    553                field.getName());
    554        writeField(field, (Object) null, value, forceAccess);
    555    }
    556
    557    /**
    558     * Writes a named {@code public static} {@link Field}. Superclasses will be considered.
    559     *
    560     * @param cls
    561     *            {@link Class} on which the field is to be found
    562     * @param fieldName
    563     *            to write
    564     * @param value
    565     *            to set
    566     * @throws IllegalArgumentException
    567     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
    568     *             not {@code static}, or {@code value} is not assignable
    569     * @throws IllegalAccessException
    570     *             if the field is not {@code public} or is {@code final}
    571     */
    572    public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
    573        writeStaticField(cls, fieldName, value, false);
    574    }
    575
    576    /**
    577     * Writes a named {@code static} {@link Field}. Superclasses will be considered.
    578     *
    579     * @param cls
    580     *            {@link Class} on which the field is to be found
    581     * @param fieldName
    582     *            to write
    583     * @param value
    584     *            to set
    585     * @param forceAccess
    586     *            whether to break scope restrictions using the
    587     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    588     *            match {@code public} fields.
    589     * @throws IllegalArgumentException
    590     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
    591     *             not {@code static}, or {@code value} is not assignable
    592     * @throws IllegalAccessException
    593     *             if the field is not made accessible or is {@code final}
    594     */
    595    public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
    596            throws IllegalAccessException {
    597        final Field field = getField(cls, fieldName, forceAccess);
    598        Validate.notNull(field, "Cannot locate field %s on %s", fieldName, cls);
    599        // already forced access above, don't repeat it here:
    600        writeStaticField(field, value, false);
    601    }
    602
    603    /**
    604     * Writes a named {@code public static} {@link Field}. Only the specified class will be considered.
    605     *
    606     * @param cls
    607     *            {@link Class} on which the field is to be found
    608     * @param fieldName
    609     *            to write
    610     * @param value
    611     *            to set
    612     * @throws IllegalArgumentException
    613     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
    614     *             not {@code static}, or {@code value} is not assignable
    615     * @throws IllegalAccessException
    616     *             if the field is not {@code public} or is {@code final}
    617     */
    618    public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
    619        writeDeclaredStaticField(cls, fieldName, value, false);
    620    }
    621
    622    /**
    623     * Writes a named {@code static} {@link Field}. Only the specified class will be considered.
    624     *
    625     * @param cls
    626     *            {@link Class} on which the field is to be found
    627     * @param fieldName
    628     *            to write
    629     * @param value
    630     *            to set
    631     * @param forceAccess
    632     *            whether to break scope restrictions using the {@code AccessibleObject#setAccessible(boolean)} method.
    633     *            {@code false} will only match {@code public} fields.
    634     * @throws IllegalArgumentException
    635     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
    636     *             not {@code static}, or {@code value} is not assignable
    637     * @throws IllegalAccessException
    638     *             if the field is not made accessible or is {@code final}
    639     */
    640    public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
    641            throws IllegalAccessException {
    642        final Field field = getDeclaredField(cls, fieldName, forceAccess);
    643        Validate.notNull(field, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
    644        // already forced access above, don't repeat it here:
    645        writeField(field, (Object) null, value, false);
    646    }
    647
    648    /**
    649     * Writes an accessible {@link Field}.
    650     *
    651     * @param field
    652     *            to write
    653     * @param target
    654     *            the object to call on, may be {@code null} for {@code static} fields
    655     * @param value
    656     *            to set
    657     * @throws IllegalAccessException
    658     *             if the field or target is {@code null}, the field is not accessible or is {@code final}, or
    659     *             {@code value} is not assignable
    660     */
    661    public static void writeField(final Field field, final Object target, final Object value) throws IllegalAccessException {
    662        writeField(field, target, value, false);
    663    }
    664
    665    /**
    666     * Writes a {@link Field}.
    667     *
    668     * @param field
    669     *            to write
    670     * @param target
    671     *            the object to call on, may be {@code null} for {@code static} fields
    672     * @param value
    673     *            to set
    674     * @param forceAccess
    675     *            whether to break scope restrictions using the
    676     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    677     *            match {@code public} fields.
    678     * @throws IllegalArgumentException
    679     *             if the field is {@code null} or {@code value} is not assignable
    680     * @throws IllegalAccessException
    681     *             if the field is not made accessible or is {@code final}
    682     */
    683    public static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess)
    684            throws IllegalAccessException {
    685        Validate.notNull(field, "The field must not be null");
    686        if (forceAccess && !field.isAccessible()) {
    687            field.setAccessible(true);
    688        } else {
    689            MemberUtils.setAccessibleWorkaround(field);
    690        }
    691        field.set(target, value);
    692    }
    693
    694    /**
    695     * Removes the final modifier from a {@link Field}.
    696     *
    697     * @param field
    698     *            to remove the final modifier
    699     * @throws IllegalArgumentException
    700     *             if the field is {@code null}
    701     * @since 3.2
    702     */
    703    public static void removeFinalModifier(final Field field) {
    704        removeFinalModifier(field, true);
    705    }
    706
    707    /**
    708     * Removes the final modifier from a {@link Field}.
    709     *
    710     * @param field
    711     *            to remove the final modifier
    712     * @param forceAccess
    713     *            whether to break scope restrictions using the
    714     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    715     *            match {@code public} fields.
    716     * @throws IllegalArgumentException
    717     *             if the field is {@code null}
    718     * @deprecated As of Java 12, we can no longer drop the {@code final} modifier, thus
    719     *             rendering this method obsolete. The JDK discussion about this change can be found
    720     *             here: http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-November/056486.html
    721     * @since 3.3
    722     */
    723    @Deprecated
    724    public static void removeFinalModifier(final Field field, final boolean forceAccess) {
    725        Validate.notNull(field, "The field must not be null");
    726
    727        try {
    728            if (Modifier.isFinal(field.getModifiers())) {
    729                // Do all JREs implement Field with a private ivar called "modifiers"?
    730                final Field modifiersField = Field.class.getDeclaredField("modifiers");
    731                final boolean doForceAccess = forceAccess && !modifiersField.isAccessible();
    732                if (doForceAccess) {
    733                    modifiersField.setAccessible(true);
    734                }
    735                try {
    736                    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    737                } finally {
    738                    if (doForceAccess) {
    739                        modifiersField.setAccessible(false);
    740                    }
    741                }
    742            }
    743        } catch (final NoSuchFieldException | IllegalAccessException ignored) {
    744            if (SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_12)) {
    745              throw new UnsupportedOperationException(
    746                  "In java 12+ final cannot be removed.",
    747                  ignored
    748              );
    749            }
    750            // else no exception is thrown because we can modify final.
    751        }
    752    }
    753
    754    /**
    755     * Writes a {@code public} {@link Field}. Superclasses will be considered.
    756     *
    757     * @param target
    758     *            the object to reflect, must not be {@code null}
    759     * @param fieldName
    760     *            the field name to obtain
    761     * @param value
    762     *            to set
    763     * @throws IllegalArgumentException
    764     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
    765     *             {@code value} is not assignable
    766     * @throws IllegalAccessException
    767     *             if the field is not accessible
    768     */
    769    public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
    770        writeField(target, fieldName, value, false);
    771    }
    772
    773    /**
    774     * Writes a {@link Field}. Superclasses will be considered.
    775     *
    776     * @param target
    777     *            the object to reflect, must not be {@code null}
    778     * @param fieldName
    779     *            the field name to obtain
    780     * @param value
    781     *            to set
    782     * @param forceAccess
    783     *            whether to break scope restrictions using the
    784     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    785     *            match {@code public} fields.
    786     * @throws IllegalArgumentException
    787     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
    788     *             {@code value} is not assignable
    789     * @throws IllegalAccessException
    790     *             if the field is not made accessible
    791     */
    792    public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
    793            throws IllegalAccessException {
    794        Validate.notNull(target, "target object must not be null");
    795        final Class<?> cls = target.getClass();
    796        final Field field = getField(cls, fieldName, forceAccess);
    797        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
    798        // already forced access above, don't repeat it here:
    799        writeField(field, target, value, false);
    800    }
    801
    802    /**
    803     * Writes a {@code public} {@link Field}. Only the specified class will be considered.
    804     *
    805     * @param target
    806     *            the object to reflect, must not be {@code null}
    807     * @param fieldName
    808     *            the field name to obtain
    809     * @param value
    810     *            to set
    811     * @throws IllegalArgumentException
    812     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
    813     *             {@code value} is not assignable
    814     * @throws IllegalAccessException
    815     *             if the field is not made accessible
    816     */
    817    public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
    818        writeDeclaredField(target, fieldName, value, false);
    819    }
    820
    821    /**
    822     * Writes a {@code public} {@link Field}. Only the specified class will be considered.
    823     *
    824     * @param target
    825     *            the object to reflect, must not be {@code null}
    826     * @param fieldName
    827     *            the field name to obtain
    828     * @param value
    829     *            to set
    830     * @param forceAccess
    831     *            whether to break scope restrictions using the
    832     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    833     *            match {@code public} fields.
    834     * @throws IllegalArgumentException
    835     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
    836     *             {@code value} is not assignable
    837     * @throws IllegalAccessException
    838     *             if the field is not made accessible
    839     */
    840    public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
    841            throws IllegalAccessException {
    842        Validate.notNull(target, "target object must not be null");
    843        final Class<?> cls = target.getClass();
    844        final Field field = getDeclaredField(cls, fieldName, forceAccess);
    845        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
    846        // already forced access above, don't repeat it here:
    847        writeField(field, target, value, false);
    848    }
    849}