Refleksi generik mendapatkan nilai bidang

132

Saya mencoba menerima nilai bidang melalui refleksi. Masalahnya adalah saya tidak tahu tipe field dan harus memutuskannya sembari mendapatkan nilainya.

Kode ini dihasilkan dengan pengecualian ini:

Tidak dapat mengatur java.lang.String field com .... fieldName ke 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);

Saya mencoba melakukan cast, tetapi saya mendapatkan kesalahan kompilasi:

field.get((targetType)objectValue)

atau

targetType objectValue = targetType.newInstance();

Bagaimana saya bisa melakukan ini?

Ido Barash
sumber
4
Melihat API , argumen field.get()seharusnya object, bukan objectValue.
akaIDIOT

Jawaban:

144

Seperti dijawab sebelumnya, Anda harus menggunakan:

Object value = field.get(objectInstance);

Cara lain, yang kadang-kadang lebih disukai, adalah memanggil pengambil dengan dinamis. contoh kode:

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

Perlu diketahui juga bahwa ketika kelas Anda mewarisi dari kelas lain, Anda perlu secara rekursif menentukan bidang. misalnya, untuk mengambil semua Fields dari kelas yang diberikan;

    for (Class<?> c = someClass; c != null; c = c.getSuperclass())
    {
        Field[] fields = c.getDeclaredFields();
        for (Field classField : fields)
        {
            result.add(classField);
        }
    }
Marius
sumber
1
tampaknya tidak sepenuhnya benar bahwa Anda perlu melakukan iterasi melalui kelas super sendiri. C.getFields () atau c.getField () akan secara otomatis mencari bidang pada setiap antarmuka implement dan secara rekursif melalui semua superclasses. Jadi cukup untuk beralih ke getX dari getDeclaredX.
Przemysław Ładyński
3
Memang, rutin getFields () akan memungkinkan Anda untuk mengambil bidang untuk semua kelas dan antarmuka super, tetapi hanya yang umum. Biasanya, bidang dibuat pribadi / dilindungi dan diekspos melalui getter / setter.
Marius
@Marius, boleh saya tahu paket apa itu BaseValidationObject?
randytan
@ Randytan, yang terkandung dalam repositori kode pribadi saya, Anda dapat menggantinya dengan Object. Hal yang sama berlaku untuk panggilan Logger statis, ganti dengan logger Anda sendiri (contoh).
Marius
@Marius objectkelas tidak memiliki metodegetMethods() . Adakah saran?
Randand
127

Anda harus lulus objek untuk mendapatkan metode bidang , jadi

  Field field = object.getClass().getDeclaredField(fieldName);    
  field.setAccessible(true);
  Object value = field.get(object);
Dmitry Spikhalskiy
sumber
6
apakah Anda tahu alasan mengapa objek harus digunakan di field.get (objek) - bidang itu sendiri berasal dari objek itu, mengapa perlu lagi!?
serup
18
@serup Tidak, Objek bidang berasal dari objek Kelas, yang tidak memiliki koneksi dengan instance Anda yang sebenarnya. ( object.getClass()akan mengembalikan Anda objek Kelas ini)
Dmitry Spikhalskiy
1
objectdalam cuplikan tidak ditentukan sehingga pembaca tidak dapat memahami cara menggunakannya.
Ghilteras
@Ghilteras dalam hal ini, mereka seharusnya tidak menggunakan refleksi dan mendapatkan beberapa keterampilan dasar terlebih dahulu 🤷🏻‍♂️. Refleksi adalah topik yang cukup canggih untuk tidak menjelaskan bahwa variabel objectberarti objek target kita / contoh kita bekerja dengan. Saya pikir pembaca sebenarnya baik-baik saja dengan apa yang ada objectdalam jawaban ini.
Dmitry Spikhalskiy
@RajanPrasad Tidak juga. Ada satu objek dalam pertanyaan yang memiliki nama 'objek'. Objek lain memiliki nama lain. Jawabannya tepat dan disesuaikan untuk pertanyaan dan untuk nama-nama yang digunakan dalam pertanyaan untuk membuat hal-hal sejelas mungkin. Jika tidak berhasil untuk Anda - saya tidak tahu bagaimana membuatnya lebih jelas dan Anda harus mencoba jawaban lain atau mungkin menghindari refleksi dulu.
Dmitry Spikhalskiy
19

Saya menggunakan refleksi dalam implementasi toString () dari kelas preferensi saya untuk melihat anggota kelas dan nilai-nilai (debugging sederhana dan cepat).

Kode sederhana yang saya gunakan:

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

Saya berharap itu akan membantu seseorang, karena saya juga telah mencari.

silversmurf
sumber
12

Meskipun tidak terlalu jelas bagi saya apa yang ingin Anda capai, saya melihat kesalahan yang jelas dalam kode Anda: Field.get()mengharapkan objek yang berisi bidang sebagai argumen, bukan nilai (mungkin) dari bidang itu. Jadi kamu harus punya field.get(object).

Karena Anda tampaknya mencari nilai bidang, Anda dapat memperolehnya sebagai:

Object objectValue = field.get(object);

Tidak perlu instantiate jenis bidang dan membuat beberapa nilai kosong / default; atau mungkin ada sesuatu yang saya lewatkan.

Costi Ciudatu
sumber
2
objecttidak ditentukan, pembaca tidak dapat memahami bagaimana menerapkan jawabannya.
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();
 }
Rahul sharma
sumber
4

Anda memanggil dapatkan dengan argumen yang salah.

Harus:

Object value = field.get(object);
Seba
sumber
2
objecttidak didefinisikan, pembaca tidak dapat memahami bagaimana menerapkan contoh dalam jawaban
Ghilteras
2

Saya memposting solusi saya di Kotlin, tetapi dapat bekerja dengan objek java juga. Saya membuat ekstensi fungsi sehingga objek apa pun dapat menggunakan fungsi ini.

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

Lihatlah halaman web ini: https://www.geeksforgeeks.org/field-get-method-in-java-with-examples/

Isaias Carrera
sumber
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));
`
BARI RANAJEET
sumber