Refleksi Java - dampak setAccessible (true)

106

Saya menggunakan beberapa anotasi untuk secara dinamis mengatur nilai bidang di kelas. Karena saya ingin melakukan ini terlepas dari apakah itu publik, dilindungi, atau pribadi, saya selalu memanggil setAccessible(true)objek Field sebelum memanggil set()metode ini. Pertanyaan saya adalah seperti apa dampak setAccessible()panggilan tersebut di lapangan itu sendiri?

Lebih khusus lagi, katakan itu adalah bidang pribadi dan kumpulan kode panggilan ini setAccessible(true). Jika beberapa tempat lain dalam kode kemudian mengambil bidang yang sama melalui refleksi, apakah bidang tersebut sudah dapat diakses? Atau apakah metode getDeclaredFields()dan getDeclaredField()mengembalikan instance baru dari objek Field setiap kali?

Saya kira cara lain untuk menyatakan pertanyaannya adalah jika saya menelepon setAccessible(true), seberapa penting untuk mengembalikannya ke nilai asli setelah saya selesai?

dnc253
sumber

Jawaban:

85

Dengan setAccessible()Anda mengubah perilaku AccessibleObject, yaitu Fieldinstance, tetapi tidak bidang kelas yang sebenarnya. Berikut dokumentasi (kutipannya):

Nilai truemenunjukkan bahwa objek yang dipantulkan harus menahan pemeriksaan untuk kontrol akses bahasa Java ketika digunakan

Dan contoh yang dapat dijalankan:

public class FieldAccessible {
    public static class MyClass {
        private String theField;
    }

    public static void main(String[] args) throws Exception {
        MyClass myClass = new MyClass();
        Field field1 = myClass.getClass().getDeclaredField("theField");
        field1.setAccessible(true);
        System.out.println(field1.get(myClass)); // no exception
        Field field2 = myClass.getClass().getDeclaredField("theField");
        System.out.println(field2.get(myClass)); // IllegalAccessException
    }

}
Moritz Petersen
sumber
@PhilipRego Anda perlu menulis sendiri deklarasi impor. Saya harap Anda tahu bagaimana melakukan itu.
Moritz Petersen
Masalah yang ditemukan. Anda harus melempar atau menangani NoSuchFieldException atau induk.
Philip Rego
Ya, ini hanya contoh kode. Maksud saya, throws Exceptionjuga menangani NoSuchFieldException, tetapi Anda mungkin ingin menanganinya dengan cara yang lebih rumit.
Moritz Petersen
Saya mendapatkan Exception pada: Field field1 = myClass.getClass (). GetDeclaredField ("theField"); sehingga bahkan tidak dapat dikompilasi, yaitu setAccessible tidak masalah?
pengguna2796104
32

The getDeclaredFieldMetode harus kembali objek baru setiap kali, justru karena objek ini memiliki bisa berubah accessiblebendera. Jadi tidak perlu mengatur ulang bendera. Anda dapat menemukan detail lengkapnya di postingan blog ini .

Jörn Horstmann
sumber
3

Seperti yang ditunjukkan oleh poster lain, setAccessiblehanya berlaku untuk instance itu dari Anda java.lang.reflect.Field, jadi pengaturan aksesibilitas kembali ke keadaan semula tidak diperlukan.

Namun...

Jika Anda ingin panggilan field.setAccessible(true)Anda terus-menerus, Anda perlu menggunakan metode yang mendasari dalam java.lang.Classdan java.lang.reflect.Field. Masyarakat menghadapi metode mengirimkan salinan dari Fieldcontoh, sehingga "lupa" setelah setiap kali Anda melakukan sesuatu seperticlass.getField(name)

import java.lang.reflect.*;
import sun.reflect.FieldAccessor;

public class Reflect {
    private static Method privateGetDeclaredFields;
    private static Method getFieldAccessor;

    public static Field[] fields(Class<?> clazz) throws Exception {
        return (Field[]) privateGetDeclaredFields.invoke(clazz, false);
    }

    public static <T> T get(Object instance, Field field) throws Exception {
        return ((FieldAccessor) getFieldAccessor.invoke(field, instance)).get(instance);
    }

    public static void set(Object instance, Field field, Object value) throws Exception {
        ((FieldAccessor) getFieldAccessor.invoke(field, instance)).set(instance, value);
    }

    static {
        try {
            // These are used to access the direct Field instances instead of the copies you normally get through #getDeclaredFields.
            privateGetDeclaredFields = Class.class.getDeclaredMethod("privateGetDeclaredFields", boolean.class);
            privateGetDeclaredFields.setAccessible(true);
            getFieldAccessor = Field.class.getDeclaredMethod("getFieldAccessor", Object.class);
            getFieldAccessor.setAccessible(true);
        } catch (Exception e) {
            // Should only occur if the internals change.
            e.printStackTrace();
        }
    }
}

Pembaruan : Implementasi ini untuk Java 8, versi yang akan datang mengubah backend yang merusak ini. Konsep yang sama tetap berlaku meskipun Anda benar-benar ingin melanjutkan strategi ini.

Col-E
sumber
-1
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class PrivateVariableAcc {

    public static void main(String[] args) throws Exception {
        PrivateVarTest myClass = new PrivateVarTest();
        Field field1 = myClass.getClass().getDeclaredField("a");
        field1.setAccessible(true);
        System.out.println("This is access the private field-"
            + field1.get(myClass));
        Method mm = myClass.getClass().getDeclaredMethod("getA");
        mm.setAccessible(true);
        System.out.println("This is calling the private method-"
            + mm.invoke(myClass, null));
    }

}
RamChandra Bhakar
sumber