Wartość pola ogólnego odbicia


137

Próbuję uzyskać wartość pola poprzez refleksję. Problem polega na tym, że nie znam typu pól i muszę o tym decydować podczas pobierania wartości.

Ten kod skutkuje tym wyjątkiem:

Nie można ustawić pola java.lang.String field com .... fieldName na java.lang.String

Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);

Class<?> targetType = field.getType();
Object objectValue = targetType.newInstance();

Object value = field.get(objectValue);

Próbowałem przesyłać, ale otrzymuję błędy kompilacji:

field.get((targetType)objectValue)

lub

targetType objectValue = targetType.newInstance();

Jak mogę to zrobić?


4
Patrząc na API , argumentem field.get()powinno być object, a nie objectValue.
akaIDIOT

Odpowiedzi:


150

Jak odpowiedziałem wcześniej, powinieneś użyć:

Object value = field.get(objectInstance);

Innym sposobem, który jest czasami preferowany, jest dynamiczne wywoływanie metody pobierającej. przykładowy kod:

public static Object runGetter(Field field, BaseValidationObject o)
{
    // MZ: Find the correct method
    for (Method method : o.getMethods())
    {
        if ((method.getName().startsWith("get")) && (method.getName().length() == (field.getName().length() + 3)))
        {
            if (method.getName().toLowerCase().endsWith(field.getName().toLowerCase()))
            {
                // MZ: Method found, run it
                try
                {
                    return method.invoke(o);
                }
                catch (IllegalAccessException e)
                {
                    Logger.fatal("Could not determine method: " + method.getName());
                }
                catch (InvocationTargetException e)
                {
                    Logger.fatal("Could not determine method: " + method.getName());
                }

            }
        }
    }


    return null;
}

Należy również pamiętać, że gdy Twoja klasa dziedziczy po innej klasie, musisz rekurencyjnie określić Pole. na przykład, aby pobrać wszystkie pola z danej klasy;

    for (Class<?> c = someClass; c != null; c = c.getSuperclass())
    {
        Field[] fields = c.getDeclaredFields();
        for (Field classField : fields)
        {
            result.add(classField);
        }
    }

2
nie wydaje się do końca prawdą, że musisz samodzielnie przechodzić przez superklasy. Metoda c.getFields () lub c.getField () automatycznie przeszuka pole w każdym interfejsie narzędzia i rekurencyjnie przejdzie przez wszystkie nadklasy. Wystarczy więc przełączyć się na getX z getDeclaredX.
Przemysław Ładyński 25.11.2015

3
Rzeczywiście, procedura getFields () pozwoli ci pobrać pola dla wszystkich superklas i interfejsów, ale tylko tych publicznych. Zwykle pola są ustawiane jako prywatne / chronione i są udostępniane za pośrednictwem funkcji pobierających / ustawiających.
Marius

1
@Marius, czy mogę wiedzieć, co to jest pakiet BaseValidationObject?
randytan

@Randytan, który jest zawarty w moim prywatnym repozytorium kodu, możesz go zastąpić Object. To samo dotyczy statycznych wywołań Logger, zamień je na swój własny logger (instancję).
Marius

1
@Marius objectklasa nie ma metody getMethods(). Jakaś rada?
randytan

129

Aby uzyskać metodę pola , należy przekazać obiekt , więc

  Field field = object.getClass().getDeclaredField(fieldName);    
  field.setAccessible(true);
  Object value = field.get(object);

6
czy znasz powód, dla którego obiekt musi być używany w field.get (obiekt) - samo pole pochodzi z tego obiektu, dlaczego znowu go potrzebuje!?
serup

19
@serup Nie, obiekt Field pochodzi z obiektu Class, który nie ma połączenia z Twoją rzeczywistą instancją. ( object.getClass()zwróci ci ten obiekt klasy)
Dmitry Spikhalskiy

3
objectwe fragmencie nie jest zdefiniowane, więc czytelnicy nie mogą zrozumieć, jak go używać.
Ghilteras

@RajanPrasad Niezupełnie. W pytaniu jest pojedynczy obiekt, który ma nazwę „obiekt”. Inne obiekty mają inne nazwy. Odpowiedź jest precyzyjna i dostosowana do pytań oraz nazw użytych w pytaniu, aby wszystko było jak najbardziej zrozumiałe. Jeśli to nie zadziała - nie mam pojęcia, jak to wyjaśnić i powinieneś spróbować innych odpowiedzi lub prawdopodobnie na razie uniknąć refleksji.
Dmitry Spikhalskiy

20

Używam odbić w implementacji toString () mojej klasy preferencji, aby zobaczyć składowe i wartości klasy (proste i szybkie debugowanie).

Uproszczony kod, którego używam:

@Override
public String toString() {
    StringBuilder sb = new StringBuilder();

    Class<?> thisClass = null;
    try {
        thisClass = Class.forName(this.getClass().getName());

        Field[] aClassFields = thisClass.getDeclaredFields();
        sb.append(this.getClass().getSimpleName() + " [ ");
        for(Field f : aClassFields){
            String fName = f.getName();
            sb.append("(" + f.getType() + ") " + fName + " = " + f.get(this) + ", ");
        }
        sb.append("]");
    } catch (Exception e) {
        e.printStackTrace();
    }

    return sb.toString();
}

Mam nadzieję, że komuś to pomoże, bo też szukałem.


12

Chociaż nie jest dla mnie jasne, co próbujesz osiągnąć, zauważyłem oczywisty błąd w twoim kodzie: Field.get()oczekuje obiektu, który zawiera pole jako argument, a nie jakiejś (możliwej) wartości tego pola. Więc powinieneś był field.get(object).

Ponieważ wydaje się, że szukasz wartości pola, możesz to uzyskać jako:

Object objectValue = field.get(object);

Nie ma potrzeby tworzenia instancji typu pola i tworzenia pustych / domyślnych wartości; a może jest coś, co przegapiłem.


2
objectnie jest zdefiniowana, czytelnicy nie mogą zrozumieć, jak zastosować odpowiedź.
Ghilteras

10
 Integer typeValue = 0;
 try {
     Class<Types> types = Types.class;
     java.lang.reflect.Field field = types.getDeclaredField("Type");
     field.setAccessible(true);
     Object value = field.get(types);
     typeValue = (Integer) value;
 } catch (Exception e) {
     e.printStackTrace();
 }

4

Dzwonisz ze złym argumentem.

Powinno być:

Object value = field.get(object);

3
objectnie jest zdefiniowana, czytelnicy nie mogą zrozumieć, jak zastosować przykład w odpowiedzi
Ghilteras

2

Publikuję swoje rozwiązanie w Kotlinie, ale może również działać z obiektami Java. Tworzę rozszerzenie funkcji, aby każdy obiekt mógł korzystać z tej funkcji.

fun Any.iterateOverComponents() {

val fields = this.javaClass.declaredFields

fields.forEachIndexed { i, field ->

    fields[i].isAccessible = true
    // get value of the fields
    val value = fields[i].get(this)

    // print result
    Log.w("Msg", "Value of Field "
            + fields[i].name
            + " is " + value)
}}

Spójrz na tę stronę internetową: https://www.geeksforgeeks.org/field-get-method-in-java-with-examples/


1
    ` 
//Here is the example I used for get the field name also the field value
//Hope This will help to someone
TestModel model = new TestModel ("MyDate", "MyTime", "OUT");
//Get All the fields of the class
 Field[] fields = model.getClass().getDeclaredFields();
//If the field is private make the field to accessible true
fields[0].setAccessible(true);
//Get the field name
  System.out.println(fields[0].getName());
//Get the field value
System.out.println(fields[0].get(model));
`
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.