ความแตกต่างระหว่าง getFields และ getDeclaredFields ใน Java reflection คืออะไร


195

ฉันสับสนเล็กน้อยเกี่ยวกับความแตกต่างระหว่างgetFieldsวิธีการและgetDeclaredFieldsวิธีการใช้การสะท้อน Java

ฉันอ่านที่getDeclaredFieldsให้คุณเข้าถึงทุกฟิลด์ของคลาสและ getFieldsคืนค่าฟิลด์สาธารณะเท่านั้น หากเป็นกรณีนี้ทำไมจะไม่คุณก็มักจะใช้getDeclaredFields?

ใครบางคนช่วยอธิบายรายละเอียดเกี่ยวกับเรื่องนี้และอธิบายความแตกต่างระหว่างสองวิธีนี้และเมื่อใด / ทำไมคุณต้องการใช้อีกวิธีหนึ่ง


3
getFieldสามารถรับฟิลด์ที่สืบทอดมาจากซูเปอร์คลาส แต่getDeclaredFieldไม่สามารถทำได้ getDeclaredFieldจำกัด ตัวเองเป็นคลาสที่คุณเรียกใช้ฟังก์ชัน
user2336315

@ user2336315 ที่ถูกต้อง แต่getFieldไม่สามารถเข้าถึงสมาชิกส่วนตัว
Madbreaks

คำตอบ:


258

getFields ()

ทุกpublicสาขาลำดับชั้นทั้งชั้น

getDeclaredFields ()

เขตข้อมูลทั้งหมดโดยไม่คำนึงถึงความสามารถในการเข้าถึงของพวกเขา แต่สำหรับคลาสปัจจุบันเท่านั้นไม่ใช่คลาสพื้นฐานที่คลาสปัจจุบันอาจรับช่วง

ในการรับลำดับชั้นทั้งหมดฉันได้เขียนฟังก์ชั่นต่อไปนี้:

public static Iterable<Field> getFieldsUpTo(@Nonnull Class<?> startClass, 
                                   @Nullable Class<?> exclusiveParent) {

   List<Field> currentClassFields = Lists.newArrayList(startClass.getDeclaredFields());
   Class<?> parentClass = startClass.getSuperclass();

   if (parentClass != null && 
          (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
     List<Field> parentClassFields = 
         (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
     currentClassFields.addAll(parentClassFields);
   }

   return currentClassFields;
}

ชั้นมีไว้เพื่อป้องกันไม่ให้เกิดการดึงของฟิลด์จากexclusiveParent Objectอาจเป็นได้nullถ้าคุณต้องการObjectฟิลด์

ชี้แจงLists.newArrayListมาจาก Guava

ปรับปรุง

FYI รหัสดังกล่าวข้างต้นมีการเผยแพร่บน GitHub ในของฉันLibExโครงการในReflectionUtils


8
คำตอบที่ดี แต่ควรสังเกตว่าฟิลด์ส่วนตัวในซูเปอร์คลาสไม่สามารถใช้งานได้โดยอินสแตนซ์ของคลาสปัจจุบันสำหรับField#getและวิธีการที่คล้ายกัน กล่าวอีกนัยหนึ่งวิธีการนี้ไม่อนุญาตให้เข้าถึงคลาสปัจจุบันไปยังอินเทอร์เฟซส่วนตัวของ superclass ของตนในทำนองเดียวกันการคอมไพล์ทั่วไปไม่ได้
FThompson

4
@Vulcan True ยกเว้นว่ามีการเขียนโค้ดเพื่อใช้การสะท้อนเพื่อเปลี่ยนขอบเขตผ่านsetAccessibleและไม่มีตัวจัดการความปลอดภัยในสถานที่
John B

เล็กน้อย nit ควรเป็น "(ไม่ว่าการเข้าถึง)" ไม่ "(ไม่ จำกัด ขอบเขต)" ทุกสาขามีขอบเขตเดียวกัน ได้แก่ร่างกายของชั้น
yshavit

@yshavit ขอบคุณ Updated
John B

1
มันจะไม่ เนื่องจากprivateฟิลด์สามารถเข้าถึงได้ผ่านทางgetDeclaredFieldsซึ่งเป็นคลาสเฉพาะ แต่ละฟิลด์ (แม้จะมีชื่อและชนิดเดียวกัน) จะเป็นFieldกรณีที่แตกต่างกัน
John B

7

ดังที่ได้กล่าวไปแล้วClass.getDeclaredField(String)จะดูเฉพาะฟิลด์Classที่คุณโทรหา

หากคุณต้องการค้นหาFieldในClassลำดับชั้นคุณสามารถใช้ฟังก์ชั่นง่าย ๆ นี้:

/**
 * Returns the first {@link Field} in the hierarchy for the specified name
 */
public static Field getField(Class<?> clazz, String name) {
    Field field = null;
    while (clazz != null && field == null) {
        try {
            field = clazz.getDeclaredField(name);
        } catch (Exception e) {
        }
        clazz = clazz.getSuperclass();
    }
    return field;
}

สิ่งนี้มีประโยชน์ในการค้นหาprivateฟิลด์จากซูเปอร์คลาสตัวอย่างเช่น นอกจากนี้หากคุณต้องการแก้ไขค่าของมันคุณสามารถใช้มันดังนี้:

/**
 * Sets {@code value} to the first {@link Field} in the {@code object} hierarchy, for the specified name
 */
public static void setField(Object object, String fieldName, Object value) throws Exception {
    Field field = getField(object.getClass(), fieldName);
    field.setAccessible(true);
    field.set(object, value);
}

แก้ไขเล็กน้อยเพื่อยังคงโยนข้อผิดพลาดหากไม่พบเลยtry try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
Sven Dhaens

5

public Field[] getFields() throws SecurityException

ส่งคืนอาร์เรย์ที่มีวัตถุเขตข้อมูลที่สะท้อนถึงเขตข้อมูลสาธารณะที่สามารถเข้าถึงได้ทั้งหมดของชั้นเรียนหรืออินเตอร์เฟซที่แสดงโดยวัตถุชั้นนี้ องค์ประกอบในอาร์เรย์ที่ส่งคืนจะไม่เรียงลำดับและไม่ได้เรียงตามลำดับใด ๆ วิธีการนี้จะส่งกลับอาร์เรย์ที่มีความยาว 0 ถ้าคลาสหรืออินเทอร์เฟซไม่มีเขตข้อมูลสาธารณะที่สามารถเข้าถึงได้หรือถ้ามันหมายถึงคลาสอาร์เรย์ชนิดดั้งเดิมหรือเป็นโมฆะ

ถ้าวัตถุคลาสนี้แสดงคลาสวิธีนี้จะส่งคืนเขตข้อมูลสาธารณะของคลาสนี้และซูเปอร์คลาสทั้งหมดของมัน ถ้าวัตถุคลาสนี้แสดงถึงอินเทอร์เฟซวิธีการนี้จะส่งคืนเขตข้อมูลของอินเทอร์เฟซนี้และของ superinterfaces ทั้งหมด

ฟิลด์ความยาวโดยนัยสำหรับคลาสอาเรย์ไม่ได้สะท้อนให้เห็นโดยวิธีนี้ รหัสผู้ใช้ควรใช้วิธีการของคลาส Array เพื่อจัดการอาร์เรย์


public Field[] getDeclaredFields() throws SecurityException

ส่งคืนอาร์เรย์ของวัตถุเขตข้อมูลที่สะท้อนถึงเขตข้อมูลทั้งหมดที่ประกาศโดยชั้นเรียนหรืออินเตอร์เฟซที่แสดงโดยวัตถุชั้นนี้ ซึ่งรวมถึงการเข้าถึงสาธารณะได้รับการป้องกันเริ่มต้น (แพคเกจ) และเขตข้อมูลส่วนตัวแต่ไม่รวมเขตข้อมูลที่สืบทอด องค์ประกอบในอาร์เรย์ที่ส่งคืนจะไม่เรียงลำดับและไม่ได้เรียงตามลำดับใด ๆ วิธีนี้ส่งกลับอาร์เรย์ที่มีความยาว 0 ถ้าคลาสหรืออินเทอร์เฟซประกาศเขตข้อมูลไม่หรือถ้าวัตถุคลาสนี้แสดงถึงชนิดดั้งเดิมดั้งเดิมคลาสของอาร์เรย์หรือเป็นโมฆะ


และถ้าฉันต้องการฟิลด์ทั้งหมดจากคลาสแม่ทั้งหมด ต้องการรหัสบางอย่างเช่นจากhttps://stackoverflow.com/a/35103361/755804 :

public static List<Field> getAllModelFields(Class aClass) {
    List<Field> fields = new ArrayList<>();
    do {
        Collections.addAll(fields, aClass.getDeclaredFields());
        aClass = aClass.getSuperclass();
    } while (aClass != null);
    return fields;
}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.