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.StringUtils; 021import org.apache.commons.lang3.Validate; 022 023import java.lang.annotation.Annotation; 024import java.lang.reflect.Field; 025import java.lang.reflect.Modifier; 026import java.util.ArrayList; 027import java.util.List; 028 029/** 030 * Utilities for working with {@link Field}s by reflection. Adapted and refactored from the dormant [reflect] Commons 031 * sandbox component. 032 * <p> 033 * The ability is provided to break the scoping restrictions coded by the programmer. This can allow fields to be 034 * changed that shouldn't be. This facility should be used with care. 035 * 036 * @since 2.5 037 * @version $Id: FieldUtils.java 1620579 2014-08-26 11:53:51Z britter $ 038 */ 039public class FieldUtils { 040 041 /** 042 * {@link FieldUtils} instances should NOT be constructed in standard programming. 043 * <p> 044 * This constructor is {@code public} to permit tools that require a JavaBean instance to operate. 045 * </p> 046 */ 047 public FieldUtils() { 048 super(); 049 } 050 051 /** 052 * Gets an accessible {@link Field} by name respecting scope. Superclasses/interfaces will be considered. 053 * 054 * @param cls 055 * the {@link Class} to reflect, must not be {@code null} 056 * @param fieldName 057 * the field name to obtain 058 * @return the Field object 059 * @throws IllegalArgumentException 060 * if the class is {@code null}, or the field name is blank or empty 061 */ 062 public static Field getField(final Class<?> cls, final String fieldName) { 063 final Field field = getField(cls, fieldName, false); 064 MemberUtils.setAccessibleWorkaround(field); 065 return field; 066 } 067 068 /** 069 * Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be 070 * considered. 071 * 072 * @param cls 073 * the {@link Class} to reflect, must not be {@code null} 074 * @param fieldName 075 * the field name to obtain 076 * @param forceAccess 077 * whether to break scope restrictions using the 078 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only 079 * match {@code public} fields. 080 * @return the Field object 081 * @throws IllegalArgumentException 082 * if the class is {@code null}, or the field name is blank or empty or is matched at multiple places 083 * in the inheritance hierarchy 084 */ 085 public static Field getField(final Class<?> cls, final String fieldName, final boolean forceAccess) { 086 Validate.isTrue(cls != null, "The class must not be null"); 087 Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty"); 088 // FIXME is this workaround still needed? lang requires Java 6 089 // Sun Java 1.3 has a bugged implementation of getField hence we write the 090 // code ourselves 091 092 // getField() will return the Field object with the declaring class 093 // set correctly to the class that declares the field. Thus requesting the 094 // field on a subclass will return the field from the superclass. 095 // 096 // priority order for lookup: 097 // searchclass private/protected/package/public 098 // superclass protected/package/public 099 // private/different package blocks access to further superclasses 100 // implementedinterface public 101 102 // check up the superclass hierarchy 103 for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) { 104 try { 105 final Field field = acls.getDeclaredField(fieldName); 106 // getDeclaredField checks for non-public scopes as well 107 // and it returns accurate results 108 if (!Modifier.isPublic(field.getModifiers())) { 109 if (forceAccess) { 110 field.setAccessible(true); 111 } else { 112 continue; 113 } 114 } 115 return field; 116 } catch (final NoSuchFieldException ex) { // NOPMD 117 // ignore 118 } 119 } 120 // check the public interface case. This must be manually searched for 121 // incase there is a public supersuperclass field hidden by a private/package 122 // superclass field. 123 Field match = null; 124 for (final Class<?> class1 : ClassUtils.getAllInterfaces(cls)) { 125 try { 126 final Field test = class1.getField(fieldName); 127 Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s" 128 + "; a matching field exists on two or more implemented interfaces.", fieldName, cls); 129 match = test; 130 } catch (final NoSuchFieldException ex) { // NOPMD 131 // ignore 132 } 133 } 134 return match; 135 } 136 137 /** 138 * Gets an accessible {@link Field} by name respecting scope. Only the specified class will be considered. 139 * 140 * @param cls 141 * the {@link Class} to reflect, must not be {@code null} 142 * @param fieldName 143 * the field name to obtain 144 * @return the Field object 145 * @throws IllegalArgumentException 146 * if the class is {@code null}, or the field name is blank or empty 147 */ 148 public static Field getDeclaredField(final Class<?> cls, final String fieldName) { 149 return getDeclaredField(cls, fieldName, false); 150 } 151 152 /** 153 * Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be 154 * considered. 155 * 156 * @param cls 157 * the {@link Class} to reflect, must not be {@code null} 158 * @param fieldName 159 * the field name to obtain 160 * @param forceAccess 161 * whether to break scope restrictions using the 162 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only 163 * match {@code public} fields. 164 * @return the Field object 165 * @throws IllegalArgumentException 166 * if the class is {@code null}, or the field name is blank or empty 167 */ 168 public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) { 169 Validate.isTrue(cls != null, "The class must not be null"); 170 Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty"); 171 try { 172 // only consider the specified class by using getDeclaredField() 173 final Field field = cls.getDeclaredField(fieldName); 174 if (!MemberUtils.isAccessible(field)) { 175 if (forceAccess) { 176 field.setAccessible(true); 177 } else { 178 return null; 179 } 180 } 181 return field; 182 } catch (final NoSuchFieldException e) { // NOPMD 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 */ 198 public static Field[] getAllFields(final Class<?> cls) { 199 final List<Field> allFieldsList = getAllFieldsList(cls); 200 return allFieldsList.toArray(new Field[allFieldsList.size()]); 201 } 202 203 /** 204 * Gets all fields of the given class and its parents (if any). 205 * 206 * @param cls 207 * the {@link Class} to query 208 * @return an array of Fields (possibly empty). 209 * @throws IllegalArgumentException 210 * if the class is {@code null} 211 * @since 3.2 212 */ 213 public static List<Field> getAllFieldsList(final Class<?> cls) { 214 Validate.isTrue(cls != null, "The class must not be null"); 215 final List<Field> allFields = new ArrayList<Field>(); 216 Class<?> currentClass = cls; 217 while (currentClass != null) { 218 final Field[] declaredFields = currentClass.getDeclaredFields(); 219 for (final Field field : declaredFields) { 220 allFields.add(field); 221 } 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<Field>(); 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 * @since 3.3 718 */ 719 public static void removeFinalModifier(final Field field, final boolean forceAccess) { 720 Validate.isTrue(field != null, "The field must not be null"); 721 722 try { 723 if (Modifier.isFinal(field.getModifiers())) { 724 // Do all JREs implement Field with a private ivar called "modifiers"? 725 final Field modifiersField = Field.class.getDeclaredField("modifiers"); 726 final boolean doForceAccess = forceAccess && !modifiersField.isAccessible(); 727 if (doForceAccess) { 728 modifiersField.setAccessible(true); 729 } 730 try { 731 modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 732 } finally { 733 if (doForceAccess) { 734 modifiersField.setAccessible(false); 735 } 736 } 737 } 738 } catch (final NoSuchFieldException ignored) { 739 // The field class contains always a modifiers field 740 } catch (final IllegalAccessException ignored) { 741 // The modifiers field is made accessible 742 } 743 } 744 745 /** 746 * Writes a {@code public} {@link Field}. Superclasses will be considered. 747 * 748 * @param target 749 * the object to reflect, must not be {@code null} 750 * @param fieldName 751 * the field name to obtain 752 * @param value 753 * to set 754 * @throws IllegalArgumentException 755 * if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or 756 * {@code value} is not assignable 757 * @throws IllegalAccessException 758 * if the field is not accessible 759 */ 760 public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException { 761 writeField(target, fieldName, value, false); 762 } 763 764 /** 765 * Writes a {@link Field}. Superclasses will be considered. 766 * 767 * @param target 768 * the object to reflect, must not be {@code null} 769 * @param fieldName 770 * the field name to obtain 771 * @param value 772 * to set 773 * @param forceAccess 774 * whether to break scope restrictions using the 775 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only 776 * match {@code public} fields. 777 * @throws IllegalArgumentException 778 * if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or 779 * {@code value} is not assignable 780 * @throws IllegalAccessException 781 * if the field is not made accessible 782 */ 783 public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess) 784 throws IllegalAccessException { 785 Validate.isTrue(target != null, "target object must not be null"); 786 final Class<?> cls = target.getClass(); 787 final Field field = getField(cls, fieldName, forceAccess); 788 Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName); 789 // already forced access above, don't repeat it here: 790 writeField(field, target, value, false); 791 } 792 793 /** 794 * Writes a {@code public} {@link Field}. Only the specified class will be considered. 795 * 796 * @param target 797 * the object to reflect, must not be {@code null} 798 * @param fieldName 799 * the field name to obtain 800 * @param value 801 * to set 802 * @throws IllegalArgumentException 803 * if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or 804 * {@code value} is not assignable 805 * @throws IllegalAccessException 806 * if the field is not made accessible 807 */ 808 public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException { 809 writeDeclaredField(target, fieldName, value, false); 810 } 811 812 /** 813 * Writes a {@code public} {@link Field}. Only the specified class will be considered. 814 * 815 * @param target 816 * the object to reflect, must not be {@code null} 817 * @param fieldName 818 * the field name to obtain 819 * @param value 820 * to set 821 * @param forceAccess 822 * whether to break scope restrictions using the 823 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only 824 * match {@code public} fields. 825 * @throws IllegalArgumentException 826 * if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or 827 * {@code value} is not assignable 828 * @throws IllegalAccessException 829 * if the field is not made accessible 830 */ 831 public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess) 832 throws IllegalAccessException { 833 Validate.isTrue(target != null, "target object must not be null"); 834 final Class<?> cls = target.getClass(); 835 final Field field = getDeclaredField(cls, fieldName, forceAccess); 836 Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName); 837 // already forced access above, don't repeat it here: 838 writeField(field, target, value, false); 839 } 840}