Apa perbedaan antara getFields dan getDeclaredFields di Java refleksi

194

Saya sedikit bingung tentang perbedaan antara getFieldsmetode dan getDeclaredFieldsmetode ketika menggunakan refleksi Java.

Saya membaca yang getDeclaredFieldsmemberi Anda akses ke semua bidang kelas dan yang getFieldshanya mengembalikan bidang publik. Jika ini masalahnya, mengapa Anda tidak selalu menggunakannya getDeclaredFields?

Dapatkah seseorang tolong uraikan hal ini, dan jelaskan perbedaan antara kedua metode, dan kapan / mengapa Anda ingin menggunakan yang satu di atas yang lain?

BlackHatSamurai
sumber
3
getFieldbisa mendapatkan bidang yang diwarisi dari superclass tetapi getDeclaredFieldtidak bisa. getDeclaredFieldbatasi sendiri ke kelas tempat Anda memanggil fungsi.
user2336315
@ user2336315 yang benar, namun getFieldtidak dapat mengakses anggota pribadi
Madbreaks

Jawaban:

258

getFields ()

Semua publicbidang naik seluruh hirarki kelas.

getDeclaredFields ()

Semua bidang, terlepas dari aksesibilitasnya tetapi hanya untuk kelas saat ini, bukan kelas dasar yang berasal dari kelas saat ini.

Untuk mendapatkan semua bidang hierarki, saya telah menulis fungsi berikut:

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;
}

The exclusiveParentkelas disediakan untuk mencegah pengambilan bidang dari Object. Mungkin nulljika Anda menginginkan Objectbidang.

Untuk memperjelas, Lists.newArrayListberasal dari Jambu.

Memperbarui

FYI, kode di atas diterbitkan di GitHub dalam proyek LibEx saya di ReflectionUtils .

John B
sumber
8
Jawaban yang bagus, tetapi harus dicatat bahwa bidang pribadi dalam superclasses tidak dapat digunakan oleh instance dari kelas saat ini untuk Field#getdan metode serupa. Dengan kata lain, pendekatan ini tidak memungkinkan akses kelas saat ini ke antarmuka pribadi superclass-nya, dengan cara yang sama kompilasi tipikal tidak.
FThompson
4
@ Vulcan Benar kecuali kode tersebut ditulis menggunakan refleksi untuk mengubah ruang lingkup melalui setAccessibledan tidak ada Manajer Keamanan di tempat
John B
Sedikit nit, harus "(tidak peduli aksesibilitas)" tidak "(tidak peduli ruang lingkup)". Semua bidang memiliki cakupan yang sama, yaitu tubuh kelas .
yshavit
@yshavit Terima kasih. Diperbarui.
John B
1
Tidak akan. Karena privatebidang hanya dapat diakses melalui getDeclaredFieldskelas khusus. Setiap bidang (bahkan dengan jenis dan nama yang sama) akan menjadi Fieldcontoh yang berbeda .
John B
7

Seperti yang telah disebutkan, Class.getDeclaredField(String)hanya melihat bidang dari Classmana Anda menyebutnya.

Jika Anda ingin mencari Fielddalam Classhierarki, Anda dapat menggunakan fungsi sederhana ini:

/**
 * 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;
}

Ini berguna untuk menemukan privatebidang dari superclass, misalnya. Juga, jika Anda ingin mengubah nilainya, Anda dapat menggunakannya seperti ini:

/**
 * 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);
}
IvanRF
sumber
sedikit modifikasi untuk tetap melempar kesalahan jika tidak ditemukan sama sekalitry try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
Sven Dhaens
5

public Field[] getFields() throws SecurityException

Mengembalikan array yang berisi objek Field yang mencerminkan semua bidang publik yang dapat diakses dari kelas atau antarmuka yang diwakili oleh objek Class ini. Elemen-elemen dalam array yang dikembalikan tidak diurutkan dan tidak dalam urutan tertentu. Metode ini mengembalikan array dengan panjang 0 jika kelas atau antarmuka tidak memiliki bidang publik yang dapat diakses, atau jika itu mewakili kelas array, tipe primitif, atau batal.

Secara khusus, jika objek Kelas ini mewakili kelas, metode ini mengembalikan bidang publik dari kelas ini dan semua superclassesnya. Jika objek Class ini mewakili antarmuka, metode ini mengembalikan bidang antarmuka ini dan semua antarmuka supernya.

Bidang panjang implisit untuk kelas array tidak tercermin oleh metode ini. Kode pengguna harus menggunakan metode class Array untuk memanipulasi array.


public Field[] getDeclaredFields() throws SecurityException

Mengembalikan array objek Bidang yang mencerminkan semua bidang yang dideklarasikan oleh kelas atau antarmuka yang diwakili oleh objek Kelas ini. Ini termasuk publik, dilindungi, akses default (paket), dan bidang pribadi , tetapi tidak termasuk bidang yang diwarisi . Elemen-elemen dalam array yang dikembalikan tidak diurutkan dan tidak dalam urutan tertentu. Metode ini mengembalikan array dengan panjang 0 jika kelas atau antarmuka menyatakan tidak ada bidang, atau jika objek Kelas ini mewakili tipe primitif, kelas array, atau batal.


Dan bagaimana jika saya membutuhkan semua bidang dari semua kelas induk? Beberapa kode diperlukan, misalnya dari 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;
}
18446744073709551615
sumber