Java Reflection kullanarak devralınan öznitelik adlarını / değerlerini alma


128

'ParentObj'den genişletilmiş bir' ChildObj 'Java nesnem var. Şimdi, Java yansıtma mekanizmasını kullanarak, ChildObj'nin tüm öznitelik adlarını ve değerlerini, devralınan öznitelikler de dahil olmak üzere almak mümkünse?

Class.getFields bana genel öznitelik dizisini verir ve Class.getDeclaredFields bana tüm alanların dizisini verir, ancak hiçbiri devralınan alanlar listesini içermez .

Devralınan nitelikleri de almanın bir yolu var mı?

Yanıtlar:


174

hayır, kendin yazmalısın. Class.getSuperClass () üzerinde çağrılan basit bir özyinelemeli yöntemdir :

public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
    fields.addAll(Arrays.asList(type.getDeclaredFields()));

    if (type.getSuperclass() != null) {
        getAllFields(fields, type.getSuperclass());
    }

    return fields;
}

@Test
public void getLinkedListFields() {
    System.out.println(getAllFields(new LinkedList<Field>(), LinkedList.class));
}

2
Evet. bunu düşündüm. ama bunu yapmanın başka bir yolu olup olmadığını kontrol etmek istedim. Teşekkürler. :)
Veera

7
Değişken bir argümanı aktarmak ve onu geri getirmek muhtemelen harika bir tasarım değildir. fields.addAll (type.getDeclaredFields ()); eklemeli gelişmiş for döngüsünden daha geleneksel olacaktır.
Tom Hawtin - tackline

En azından onu derleme (stackoverflow'da!) Ve muhtemelen küçük bir Arrays.asList ekleme ihtiyacı hissediyordum.
Tom Hawtin - tackline

Görünüşe göre kodunuz tüm alanları, ayrıca devralınmayan özel ve statik alanları da topluyor.
Peter Verhas

90
    public static List<Field> getAllFields(Class<?> type) {
        List<Field> fields = new ArrayList<Field>();
        for (Class<?> c = type; c != null; c = c.getSuperclass()) {
            fields.addAll(Arrays.asList(c.getDeclaredFields()));
        }
        return fields;
    }

9
Bu benim tercih ettiğim çözümdür, ancak buna "getAllFields" diyecektim çünkü verilen sınıfın alanlarını da döndürüyor.
Pino

3
Özyinelemeyi çok sevmeme rağmen (eğlenceli!), Bu yöntemin okunabilirliğini ve daha sezgisel parametreleri tercih ederim (yeni bir koleksiyonun geçmesi gerekmez), artık if (for cümlesinde örtük) ve alanlar üzerinde yineleme yok kendilerini.
Remi Morin

özyinelemeli gereksizdir ve .. Kısa kodları severim! Teşekkürler! :)
Aquarius Power

Uzun yıllar boyunca, için başlangıç ​​değerinin sadece bir tamsayı olduğunu düşünüyorum, @ Veera'nın sorusuyla, bence sadece özyinelemeli bunu çözebilir, @ Esko Luontola komutunuz harika.
Touya Akira

@Esko: Çok teşekkür ederim. Günü kurtardı! Özlü ve kusursuz çalışıyor!
gaurav

37

Bunun yerine, bunu başarmak için bir kitaplığa güvenmek isterseniz, Apache Commons Lang sürüm 3.2+ FieldUtils.getAllFieldsListşunları sağlar :

import java.lang.reflect.Field;
import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.AbstractSequentialList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.Assert;
import org.junit.Test;

public class FieldUtilsTest {

    @Test
    public void testGetAllFieldsList() {

        // Get all fields in this class and all of its parents
        final List<Field> allFields = FieldUtils.getAllFieldsList(LinkedList.class);

        // Get the fields form each individual class in the type's hierarchy
        final List<Field> allFieldsClass = Arrays.asList(LinkedList.class.getFields());
        final List<Field> allFieldsParent = Arrays.asList(AbstractSequentialList.class.getFields());
        final List<Field> allFieldsParentsParent = Arrays.asList(AbstractList.class.getFields());
        final List<Field> allFieldsParentsParentsParent = Arrays.asList(AbstractCollection.class.getFields());

        // Test that `getAllFieldsList` did truly get all of the fields of the the class and all its parents 
        Assert.assertTrue(allFields.containsAll(allFieldsClass));
        Assert.assertTrue(allFields.containsAll(allFieldsParent));
        Assert.assertTrue(allFields.containsAll(allFieldsParentsParent));
        Assert.assertTrue(allFields.containsAll(allFieldsParentsParentsParent));
    }
}

6
Boom! Tekerleği yeniden icat etmemeyi seviyorum. Bunun için şerefe.
Joshua Pinter

6

Aramalısın:

Class.getSuperclass().getDeclaredFields()

Miras hiyerarşisini gerektiği gibi yinelemek.


5

Yansımalar kitaplığını kullanın:

public Set<Field> getAllFields(Class<?> aClass) {
    return org.reflections.ReflectionUtils.getAllFields(aClass);
}

4

Özyinelemeli çözümler tamam, tek küçük sorun, bildirilen ve miras alınan üyelerin bir üst kümesini döndürmeleridir. GetDeclaredFields () yönteminin özel yöntemler de döndürdüğünü unutmayın. Böylece, tüm üst sınıf hiyerarşisinde gezindiğiniz için, üst sınıflarda bildirilen tüm özel alanları dahil edersiniz ve bunlar miras alınmaz.

Modifier.isPublic ile basit bir filtre || Modifier.isProtected yüklem şunları yapacaktır:

import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isProtected;

(...)

List<Field> inheritableFields = new ArrayList<Field>();
for (Field field : type.getDeclaredFields()) {
    if (isProtected(field.getModifiers()) || isPublic(field.getModifiers())) {
       inheritableFields.add(field);
    }
}

2
private static void addDeclaredAndInheritedFields(Class<?> c, Collection<Field> fields) {
    fields.addAll(Arrays.asList(c.getDeclaredFields())); 
    Class<?> superClass = c.getSuperclass(); 
    if (superClass != null) { 
        addDeclaredAndInheritedFields(superClass, fields); 
    }       
}

Yukarıdaki "DidYouMeanThatTomHa ..." çözümünün çalışan sürümü


2

Yay kullanım kitaplığı ile, sınıfta belirli bir özelliğin olup olmadığını kontrol etmek için kullanabilirsiniz:

Field field = ReflectionUtils.findRequiredField(YOUR_CLASS.class, "ATTRIBUTE_NAME");

log.info(field2.getName());

Api belgesi:
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/util/ReflectionUtils.html

veya

 Field field2 = ReflectionUtils.findField(YOUR_CLASS.class, "ATTRIBUTE_NAME");

 log.info(field2.getName());

Api belgesi:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ReflectionUtils.html

@cheers


1

Deneyebilirsin:

   Class parentClass = getClass().getSuperclass();
   if (parentClass != null) {
      parentClass.getDeclaredFields();
   }

1

Daha kısa ve daha az nesne örneklenmiş mi? ^^

private static Field[] getAllFields(Class<?> type) {
    if (type.getSuperclass() != null) {
        return (Field[]) ArrayUtils.addAll(getAllFields(type.getSuperclass()), type.getDeclaredFields());
    }
    return type.getDeclaredFields();
}

HI @Alexis LEGROS: ArrayUtils sembolü bulamıyor.
Touya Akira

1
Bu sınıf Apache Commons Lang'dandır.
Alexis LEGROS

Apache'nin bu soru isteğini işlemek için zaten bir FieldUtils.getAllFields işlevi vardır.
Touya Akira

1

getFields (): tüm sınıf hiyerarşisi ve yukarı tüm kamu alanları alır
: getDeclaredFields () bakılmaksızın modifiye edici ama yalnızca geçerli sınıf için, tüm alanları alır. Yani, dahil olan tüm hiyerarşiyi almalısınız.
Son zamanlarda bu kodu org.apache.commons.lang3.reflect.FieldUtils adresinden gördüm.

public static List<Field> getAllFieldsList(final Class<?> cls) {
        Validate.isTrue(cls != null, "The class must not be null");
        final List<Field> allFields = new ArrayList<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            final Field[] declaredFields = currentClass.getDeclaredFields();
            Collections.addAll(allFields, declaredFields);
            currentClass = currentClass.getSuperclass();
        }
        return allFields;
}

0
private static void addDeclaredAndInheritedFields(Class c, Collection<Field> fields) {
    fields.addAll(Arrays.asList(c.getDeclaredFields()));
    Class superClass = c.getSuperclass();
    if (superClass != null) {
        addDeclaredAndInheritedFields(superClass, fields);
    }
}

0

Bu, @ user1079877 tarafından kabul edilen cevabın yeniden yazılmasıdır. İşlevin bir parametresini değiştirmeyen ve ayrıca bazı modern Java özelliklerini kullanan bir sürüm olabilir.

public <T> Field[] getFields(final Class<T> type, final Field... fields) {
    final Field[] items = Stream.of(type.getDeclaredFields(), fields).flatMap(Stream::of).toArray(Field[]::new);
    if (type.getSuperclass() == null) {
        return items;
    } else {
        return getFields(type.getSuperclass(), items);
    }
}

Bu uygulama aynı zamanda çağrıyı biraz daha özlü hale getirir:

var fields = getFields(MyType.class);

0

FieldUtils tarafından ele alınmayan birkaç tuhaflık vardır - özellikle sentetik alanlar (örneğin, JaCoCo tarafından enjekte edilen) ve ayrıca bir enum türünün her örnek için bir alanı olduğu gerçeği ve bir nesne grafiğini geziyorsanız, tüm alanlar ve sonra her birinin alanlarını vb. alırsanız, bir numaralandırmaya bastığınızda sonsuz bir döngüye girersiniz. Genişletilmiş bir çözüm (ve dürüst olmak gerekirse, bunun bir kütüphanede bir yerde yaşaması gerektiğine eminim!) Şöyle olacaktır:

/**
 * Return a list containing all declared fields and all inherited fields for the given input
 * (but avoiding any quirky enum fields and tool injected fields).
 */
public List<Field> getAllFields(Object input) {
    return getFieldsAndInheritedFields(new ArrayList<>(), input.getClass());
}

private List<Field> getFieldsAndInheritedFields(List<Field> fields, Class<?> inputType) {
    fields.addAll(getFilteredDeclaredFields(inputType));
    return inputType.getSuperclass() == null ? fields : getFieldsAndInheritedFields(fields, inputType.getSuperclass());

}

/**
 * Where the input is NOT an {@link Enum} type then get all declared fields except synthetic fields (ie instrumented
 * additional fields). Where the input IS an {@link Enum} type then also skip the fields that are all the
 * {@link Enum} instances as this would lead to an infinite loop if the user of this class is traversing
 * an object graph.
 */
private List<Field> getFilteredDeclaredFields(Class<?> inputType) {
    return Arrays.asList(inputType.getDeclaredFields()).stream()
                 .filter(field -> !isAnEnum(inputType) ||
                         (isAnEnum(inputType) && !isSameType(field, inputType)))
                 .filter(field -> !field.isSynthetic())
                 .collect(Collectors.toList());

}

private boolean isAnEnum(Class<?> type) {
    return Enum.class.isAssignableFrom(type);
}

private boolean isSameType(Field input, Class<?> ownerType) {
    return input.getType().equals(ownerType);
}

Spock'ta test sınıfı (ve Groovy sentetik alanlar ekler):

class ReflectionUtilsSpec extends Specification {

    def "declared fields only"() {

        given: "an instance of a class that does not inherit any fields"
        def instance = new Superclass()

        when: "all fields are requested"
        def result = new ReflectionUtils().getAllFields(instance)

        then: "the fields declared by that instance's class are returned"
        result.size() == 1
        result.findAll { it.name in ['superThing'] }.size() == 1
    }


    def "inherited fields"() {

        given: "an instance of a class that inherits fields"
        def instance = new Subclass()

        when: "all fields are requested"
        def result = new ReflectionUtils().getAllFields(instance)

        then: "the fields declared by that instance's class and its superclasses are returned"
        result.size() == 2
        result.findAll { it.name in ['subThing', 'superThing'] }.size() == 2

    }

    def "no fields"() {
        given: "an instance of a class with no declared or inherited fields"
        def instance = new SuperDooperclass()

        when: "all fields are requested"
        def result = new ReflectionUtils().getAllFields(instance)

        then: "the fields declared by that instance's class and its superclasses are returned"
        result.size() == 0
    }

    def "enum"() {

        given: "an instance of an enum"
        def instance = Item.BIT

        when: "all fields are requested"
        def result = new ReflectionUtils().getAllFields(instance)

        then: "the fields declared by that instance's class and its superclasses are returned"
        result.size() == 3
        result.findAll { it.name == 'smallerItem' }.size() == 1
    }

    private class SuperDooperclass {
    }

    private class Superclass extends SuperDooperclass {
        private String superThing
    }


    private class Subclass extends Superclass {
        private String subThing
    }

    private enum Item {

        BIT("quark"), BOB("muon")

        Item(String smallerItem) {
            this.smallerItem = smallerItem
        }

        private String smallerItem

    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.