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 */ 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 NullPointerException 64 * if the class is {@code null} 65 * @throws IllegalArgumentException 66 * if the field name is {@code null}, blank, or empty 67 */ 68 public static Field getField(final Class<?> cls, final String fieldName) { 69 return MemberUtils.setAccessibleWorkaround(getField(cls, fieldName, false)); 70 } 71 72 /** 73 * Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be 74 * considered. 75 * 76 * @param cls 77 * the {@link Class} to reflect, must not be {@code null} 78 * @param fieldName 79 * the field name to obtain 80 * @param forceAccess 81 * whether to break scope restrictions using the 82 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only 83 * match {@code public} fields. 84 * @return the Field object 85 * @throws NullPointerException if the class is {@code null} 86 * @throws IllegalArgumentException if the field name is blank or empty or is matched at multiple places 87 * in the inheritance hierarchy 88 */ 89 public static Field getField(final Class<?> cls, final String fieldName, final boolean forceAccess) { 90 Objects.requireNonNull(cls, "cls"); 91 Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty"); 92 // FIXME is this workaround still needed? lang requires Java 6 93 // Sun Java 1.3 has a bugged implementation of getField hence we write the 94 // code ourselves 95 96 // getField() will return the Field object with the declaring class 97 // set correctly to the class that declares the field. Thus requesting the 98 // field on a subclass will return the field from the superclass. 99 // 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 }