Akses ke bidang warisan pribadi melalui refleksi di Jawa


109

Saya menemukan cara untuk mendapatkan anggota yang diwariskan melalui class.getDeclaredFields(); dan akses ke anggota pribadi melalui class.getFields() Tapi saya sedang mencari bidang warisan pribadi. Bagaimana saya bisa mencapai ini?


28
"bidang warisan pribadi" tidak ada. Jika suatu bidang bersifat pribadi, itu tidak diwariskan, dan tetap hanya untuk lingkup kelas induk. Untuk mengakses bidang pribadi induk, Anda harus mengakses kelas induk terlebih dahulu (lihat tanggapan aioobe)
Benoit Courtine

6
Meskipun demikian, bidang yang dilindungi diwariskan, tetapi Anda harus melakukan hal yang sama untuk mendapatkannya melalui refleksi.
Bozho

Jawaban:


128

Ini harus menunjukkan bagaimana menyelesaikannya:

import java.lang.reflect.Field;

class Super {
    private int i = 5;
}

public class B extends Super {
    public static void main(String[] args) throws Exception {
        B b = new B();
        Field f = b.getClass().getSuperclass().getDeclaredField("i");
        f.setAccessible(true);
        System.out.println(f.get(b));
    }
}

(Atau Class.getDeclaredFieldsuntuk larik dari semua bidang.)

Keluaran:

5

Apakah ini mendapatkan semua bidang superclass atau hanya superclass langsung?
Hujan

Bidang kelas super langsung. Anda dapat mengulanginya getSuperclass()sampai Anda mencapai nulljika Anda ingin naik lebih tinggi.
aioobe

Mengapa Anda tidak menggunakan getDeclaredFields()[0]atau getDeclaredField("i")tetapi mengulang [0]akses array dalam dua pernyataan berikutnya?
Holger

Itu karena cara pertanyaan khusus ini dirumuskan. Itu pada dasarnya hanya demonstrasi cara menggunakan getDeclaredFields. Jawaban telah diperbarui.
aioobe

44

Pendekatan terbaik di sini adalah menggunakan Pola Pengunjung untuk menemukan semua bidang di kelas dan semua kelas super dan menjalankan tindakan panggilan balik padanya.


Penerapan

Spring memiliki kelas Utility yang bagus ReflectionUtilsyang melakukan hal itu: ia mendefinisikan metode untuk mengulang semua bidang dari semua kelas super dengan callback:ReflectionUtils.doWithFields()

Dokumentasi:

Panggil callback yang diberikan pada semua bidang di kelas target, naikkan hierarki kelas untuk mendapatkan semua bidang yang dideklarasikan.

Parameter:
- clazz - kelas target yang akan dianalisis
- fc - callback yang akan dipanggil untuk setiap
kolom - ff - filter yang menentukan kolom untuk menerapkan callback

Kode sampel:

ReflectionUtils.doWithFields(RoleUnresolvedList.class,
    new FieldCallback(){

        @Override
        public void doWith(final Field field) throws IllegalArgumentException,
            IllegalAccessException{

            System.out.println("Found field " + field + " in type "
                + field.getDeclaringClass());

        }
    },
    new FieldFilter(){

        @Override
        public boolean matches(final Field field){
            final int modifiers = field.getModifiers();
            // no static fields please
            return !Modifier.isStatic(modifiers);
        }
    });

Keluaran:

Ditemukan bidang private transient boolean javax.management.relation.RoleUnresolvedList.typeSafe di kelas tipe javax.management.relation.RoleUnresolvedList
Ditemukan bidang private transient boolean javax.management.relation.RoleUnresolvedList.tainted di
bidang kelas jenis javax.management.relation private transient java.lang.Object [] java.util.ArrayList.elementData dalam jenis kelas java.util.ArrayList
Ditemukan bidang private int java.util.ArrayList.size di kelas jenis java.util.ArrayList
Ditemukan bidang dilindungi sementara int java. util.AbstractList.modCount dalam kelas tipe java.util.AbstractList


3
itu bukan "pola pengunjung", tetapi masih merupakan alat yang sangat bagus jika Anda memiliki virus Spring di kode Anda. terima kasih telah membagikannya :)
thinlizzy

2
@ jose.diego Saya cukup yakin Anda bisa berdebat tentang itu. Ini mengunjungi hierarki kelas daripada pohon objek, tetapi prinsipnya tetap sama
Sean Patrick Floyd

Tidak yakin apakah komentar ini akan mendapat tanggapan, tetapi Anda hanya mengunjungi bidang tertentu pada satu waktu dengan solusi ini. Jika saya perlu melihat bidang lain pada saat yang sama - misalnya, setel bidang ini ke "abc" jika bidang lain adalah NULL - Saya tidak memiliki objek secara keseluruhan yang tersedia untuk saya.
gen b.

Sayang sekali JavaDoc untuk kelas ini menunjukkan bahwa "ini hanya ditujukan untuk penggunaan internal", jadi ini adalah risiko yang mungkin bagi siapa saja yang ingin menggunakannya.
spaceman spiff

1
@spacemanspiff Anda secara teknis benar, tetapi kelas ini telah ada selama sekitar 15 tahun (termasuk 4 versi rilis utama) dan telah digunakan secara luas oleh banyak pelanggan Spring. Saya ragu mereka akan menariknya kembali sekarang.
Sean Patrick Floyd

34

Ini akan melakukannya:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        Collections.addAll(result, i.getDeclaredFields());
        i = i.getSuperclass();
    }

    return result;
}

Jika Anda menggunakan alat cakupan kode seperti EclEmma , Anda harus berhati-hati: mereka menambahkan bidang tersembunyi ke setiap kelas Anda. Untuk EclEmma, ​​bidang ini ditandai sintetis , dan Anda dapat memfilternya seperti ini:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        for (Field field : i.getDeclaredFields()) {
            if (!field.isSynthetic()) {
                result.add(field);
            }
        }
        i = i.getSuperclass();
    }

    return result;
}

Terima kasih atas komentar Anda tentang bidang sintetis, EMMA melakukan hal yang sama.
Anatoliy

ini dideklarasikan dan diwarisi bidang kelas argumen sehingga harus dinamai getDeclaredAndInheritedPrivateFields. sempurna meskipun terima kasih!
Peter Hawkins

1
tangkapan yang bagus di isSynthetic :)
Lucas Crawford

Terima kasih atas jawaban yang luar biasa ~
Hujan

19
public static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        try {
            Field f = tmpClass.getDeclaredField(fieldName);
            return f;
        } catch (NoSuchFieldException e) {
            tmpClass = tmpClass.getSuperclass();
        }
    } while (tmpClass != null);

    throw new RuntimeException("Field '" + fieldName
            + "' not found on class " + clazz);
}

(berdasarkan jawaban ini )


15

Sebenarnya saya menggunakan hierachy tipe kompleks sehingga solusi Anda tidak lengkap. Saya perlu melakukan panggilan rekursif untuk mendapatkan semua bidang warisan pribadi. Inilah solusi saya

 /**
 * Return the set of fields declared at all level of class hierachy
 */
public Vector<Field> getAllFields(Class clazz) {
    return getAllFieldsRec(clazz, new Vector<Field>());
}

private Vector<Field> getAllFieldsRec(Class clazz, Vector<Field> vector) {
    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        getAllFieldsRec(superClazz, vector);
    }
    vector.addAll(toVector(clazz.getDeclaredFields()));
    return vector;
}

Namun, solusinya membuat Anda berada di jalan yang benar, bukan?
aperkins

1
Vektor adalah kode lama yang buruk. Silakan gunakan struktur data saat ini dari kerangka koleksi (ArrayList memadai dalam banyak kasus)
Sean Patrick Floyd

@aperkins jawaban aioobe terlihat seperti milik saya, tetapi saya menemukannya dan kemudian saya melihat jawabannya. @seanizer Vector tidak terlalu tua, dan ini adalah anggota API koleksi
benzen

"Pada platform Java 2 v1.2, kelas ini telah dipasang untuk mengimplementasikan List, sehingga menjadi bagian dari framework kumpulan Java." dipasang di 1.2? jika itu belum tua lalu apa? Sumber: download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html
Sean Patrick Floyd

7
Vektor memiliki overhead yang besar karena semuanya disinkronkan. Dan jika Anda membutuhkan sinkronisasi, ada kelas yang lebih baik di java.util.concurrent. Vektor, Hashtable dan StringBuffer dalam banyak kasus harus diganti oleh ArrayList, HashMap dan StringBuilder
Sean Patrick Floyd

8

Saya perlu menambahkan dukungan untuk bidang warisan untuk cetak biru di Model Citizen . Saya mendapatkan metode ini yang sedikit lebih ringkas untuk mengambil bidang Kelas + bidang yang diwariskan.

private List<Field> getAllFields(Class clazz) {
    List<Field> fields = new ArrayList<Field>();

    fields.addAll(Arrays.asList(clazz.getDeclaredFields()));

    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        fields.addAll(getAllFields(superClazz));
    }

    return fields;
}

7
private static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        for ( Field field : tmpClass.getDeclaredFields() ) {
            String candidateName = field.getName();
            if ( ! candidateName.equals(fieldName) ) {
                continue;
            }
            field.setAccessible(true);
            return field;
        }
        tmpClass = tmpClass.getSuperclass();
    } while ( clazz != null );
    throw new RuntimeException("Field '" + fieldName +
        "' not found on class " + clazz);
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.