Jak odczytać wartość pola prywatnego z innej klasy w Javie?

Mam źle zaprojektowaną klasę w 3rd-party JAR i muszę uzyskać dostęp do jednego z jej pól private. Na przykład, dlaczego powinienem wybrać pole prywatne, czy jest to konieczne?

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

Jak mogę użyć reflection, aby uzyskać wartość stuffIWant?

Author: Steve Chambers, 2009-07-28

10 answers

Aby uzyskać dostęp do prywatnych pól, musisz pobrać je z zadeklarowanych pól klasy , a następnie uczynić je dostępnymi:

Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException

EDIT: jak zostało to skomentowane przez aperkins, zarówno dostęp do pola, ustawienie go jako dostępnego i pobranie wartości spowoduje wyrzucenie Exceptions, chociaż jedyne zaznaczone wyjątki, o których musisz pamiętać, są skomentowane powyżej.

NoSuchFieldException zostanie wyrzucony, jeśli poprosisz o Pole o nazwie które nie odpowiadały zadeklarowanemu polowi.

obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException

IllegalAccessException zostanie wyrzucone, jeśli pole nie jest dostępne (na przykład, jeśli jest prywatne i nie zostało udostępnione przez pominięcie linii f.setAccessible(true).

RuntimeExceptions, które mogą zostać rzucone, to SecurityException s (Jeśli JVM SecurityManager nie pozwoli Ci zmienić dostępności pola), lub IllegalArgumentException s, jeśli spróbujesz uzyskać dostęp do pola na obiekcie nie należącym do klasy pola:

f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
 559
Author: oxbow_lakes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-29 07:30:47

Spróbuj FieldUtils z Apache commons-lang3:

FieldUtils.readField(object, fieldName, true);
 114
Author: yegor256,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-25 07:40:48

Odbicie nie jest jedynym sposobem na rozwiązanie problemu (którym jest dostęp do prywatnej funkcjonalności/zachowania klasy/komponentu)

Alternatywnym rozwiązaniem jest wyodrębnienie klasy z .jar, dekompilować go używając (powiedzmy) Jode lub Jad, zmienić pole (lub dodać accesor) i przekompilować je w stosunku do oryginału .słoik. Następnie umieścić nowy .klasy przed .jar w ścieżce klasowej, lub ponownie umieścić ją w .jar. (narzędzie jar pozwala wyodrębnić i ponownie włożyć do istniejącą .jar)

Jak wspomniano poniżej, rozwiązuje to szerszy problem dostępu/zmiany stanu prywatnego, a nie po prostu dostępu/zmiany pola.

Wymaga to oczywiście nie podpisywania .jar.

 23
Author: Brian Agnew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-25 10:36:46

Jeszcze jedna opcja, o której jeszcze nie wspomniano: użyj Groovy . Groovy umożliwia dostęp do prywatnych zmiennych instancji jako efekt uboczny projektowania języka. Niezależnie od tego, czy masz getter do pola, możesz po prostu użyć

def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
 14
Author: lucas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-13 09:07:28

Używając odbicia w Javie możesz uzyskać dostęp do wszystkich private/public pól i metod jednej klasy do drugiej .Ale jak na wyrocznię dokumentacja w sekcji wady zalecali:

"ponieważ reflection pozwala kodowi wykonywać operacje, które byłyby nielegalne w kodzie nieodblaskowym, takie jak dostęp do prywatnych pól i metod, użycie reflection może skutkować nieoczekiwanymi efektami ubocznymi, które mogą sprawić, że kod będzie dysfunkcyjny i może zniszczyć przenośność. Kod odblaskowy łamie abstrakcje i dlatego może zmieniać zachowanie wraz z aktualizacjami platformy"

Oto następujące snapty kodu, aby zademonstrować podstawowe pojęcia refleksji

Reflection1.java

public class Reflection1{

    private int i = 10;

    public void methoda()
    {

        System.out.println("method1");
    }
    public void methodb()
    {

        System.out.println("method2");
    }
    public void methodc()
    {

        System.out.println("method3");
    }

}

Reflection2.java

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class Reflection2{

    public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        Method[] mthd = Reflection1.class.getMethods(); // for axis the methods 

        Field[] fld = Reflection1.class.getDeclaredFields();  // for axis the fields  

        // Loop for get all the methods in class
        for(Method mthd1:mthd)
        {

            System.out.println("method :"+mthd1.getName());
            System.out.println("parametes :"+mthd1.getReturnType());
        }

        // Loop for get all the Field in class
        for(Field fld1:fld)
        {
            fld1.setAccessible(true);
            System.out.println("field :"+fld1.getName());
            System.out.println("type :"+fld1.getType());
            System.out.println("value :"+fld1.getInt(new Reflaction1()));
        }
    }

}
Mam nadzieję, że to pomoże.
 7
Author: Simmant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-25 05:09:59

Jak wspomina oxbow_lakes, możesz użyć reflection, aby ominąć ograniczenia dostępu (zakładając, że Twój SecurityManager ci na to pozwoli).

To powiedziawszy, jeśli ta klasa jest tak źle zaprojektowana, że sprawia, że uciekasz się do takich hackerów, może powinieneś poszukać alternatywy. Na pewno ten mały hack może zaoszczędzić Ci kilka godzin, ale ile będzie cię to kosztować w dół drogi?

 5
Author: Laurence Gonsalves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-28 19:36:58

Użyj Seot Java Optimization framework do bezpośredniej modyfikacji kodu bajtowego. http://www.sable.mcgill.ca/soot/

Soot jest całkowicie napisany w Javie i działa z nowymi wersjami Javy.

 4
Author: pcpratts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-19 14:42:51

Musisz wykonać następujące czynności:

private static Field getField(Class<?> cls, String fieldName) {
    for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
        try {
            final Field field = c.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field;
        } catch (final NoSuchFieldException e) {
            // Try parent
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Cannot access field " + cls.getName() + "." + fieldName, e);
        }
    }
    throw new IllegalArgumentException(
            "Cannot find field " + cls.getName() + "." + fieldName);
}
 2
Author: Luke Hutchison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-13 09:08:11

Tylko dodatkowa uwaga na temat reflection: zaobserwowałem w niektórych szczególnych przypadkach, gdy w różnych pakietach istnieje kilka klas o tej samej nazwie, że reflection, jak to zostało użyte w top answer, może nie wybrać właściwej klasy z obiektu. Więc jeśli wiesz, co to jest paczka.klasy obiektu, wtedy lepiej jest uzyskać dostęp do jego prywatnych wartości pól w następujący sposób:

org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0);
Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver");
f.setAccessible(true);
Solver s = (Solver) f.get(ll);

(to jest przykładowa klasa, która nie działała dla mnie)

 1
Author: xtof54,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-09 07:59:42

Jeśli używasz sprężyny, Reflectestutils zapewnia kilka przydatnych narzędzi, które pomagają tutaj przy minimalnym wysiłku. Jest on opisany jako "do użytku w scenariuszach testów jednostkowych i integracyjnych" . Istnieje również podobna Klasa o nazwie ReflectionUtils , ale jest to opisane jako "przeznaczone tylko do użytku wewnętrznego" - Zobacz ta odpowiedź dla interpretacji tego, co to oznacza.

Aby zaadresować zamieszczony przykład:

Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
 0
Author: Steve Chambers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-08 12:56:01