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