Riflessione Java - impatto di setAccessible (true)


106

Sto usando alcune annotazioni per impostare dinamicamente i valori dei campi nelle classi. Poiché desidero farlo indipendentemente dal fatto che sia pubblico, protetto o privato, sto chiamando setAccessible(true)l'oggetto Field ogni volta prima di chiamare il set()metodo. La mia domanda è che tipo di impatto ha la setAccessible()chiamata sul campo stesso?

Più specificamente, diciamo che è un campo privato e questo insieme di chiamate in codice setAccessible(true). Se un altro punto nel codice dovesse poi recuperare lo stesso campo attraverso la riflessione, il campo sarebbe già accessibile? Oppure i metodi getDeclaredFields()e getDeclaredField()restituiscono ogni volta nuove istanze di un oggetto Field?

Immagino che un altro modo per affermare la domanda sia se chiamo setAccessible(true), quanto è importante riportarlo al valore originale dopo aver finito?

Risposte:


85

Con setAccessible()si modifica il comportamento di AccessibleObject, cioè l' Fieldistanza, ma non il campo effettivo della classe. Ecco la documentazione (estratto):

Un valore di trueindica che l'oggetto riflesso deve sopprimere i controlli per il controllo dell'accesso al linguaggio Java quando viene utilizzato

E un esempio eseguibile:

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
    }

}

@PhilipRego devi scrivere tu stesso le dichiarazioni di importazione. Spero che tu sappia come farlo.
Moritz Petersen

Problema riscontrato. Devi lanciare o gestire NoSuchFieldException o genitore.
Philip Rego

Sì, questo è solo un codice di esempio. Voglio dire, throws Exceptionanche le maniglie NoSuchFieldException, ma potresti volerlo gestire in un modo più elaborato.
Moritz Petersen

Ricevo l'eccezione su: Field field1 = myClass.getClass (). GetDeclaredField ("theField"); quindi non si compila nemmeno, cioè setAccessible non importa nemmeno?
user2796104

32

Il getDeclaredFieldmetodo deve restituire ogni volta un nuovo oggetto, esattamente perché questo oggetto ha il accessibleflag mutabile . Quindi non è necessario reimpostare il flag. Puoi trovare tutti i dettagli in questo post del blog .


3

Come altri poster hanno indicato, setAccessibleè applicabile solo a quell'istanza del tuo java.lang.reflect.Field, quindi non è necessario riportare l'accessibilità al suo stato originale.

Però...

Se vuoi che le tue chiamate field.setAccessible(true)siano persistenti devi usare i metodi sottostanti in java.lang.Classe java.lang.reflect.Field. Il pubblico di fronte a metodi che inviare copie del Fieldcaso, in modo che "dimentica" dopo ogni volta che si fa qualcosa di simileclass.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();
        }
    }
}

Aggiornamento : questa implementazione è per Java 8, le versioni future cambiano il back-end che lo interrompe. Lo stesso concetto si applica comunque se si desidera davvero continuare questa strategia.


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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.