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