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