जावा में प्रतिबिंब के माध्यम से निजी विरासत वाले क्षेत्रों तक पहुंच


109

मुझे विरासत में मिले सदस्यों के माध्यम से class.getDeclaredFields(); और निजी सदस्यों के माध्यम से विरासत में प्राप्त करने का एक तरीका मिला, class.getFields() लेकिन मैं निजी विरासत वाले क्षेत्रों की तलाश कर रहा हूं। इसे कैसे प्राप्त किया जा सकता है?


28
"निजी विरासत वाले क्षेत्र" मौजूद नहीं हैं। यदि कोई क्षेत्र निजी है, तो यह विरासत में नहीं मिलता है, और केवल मूल वर्ग के दायरे में रहता है। अभिभावक निजी क्षेत्रों तक पहुँचने के लिए, आपको पहले अभिभावक वर्ग का उपयोग करना होगा (cf. aioobe की प्रतिक्रिया)
Benoit Courtine

6
उस ने कहा, संरक्षित क्षेत्र विरासत में मिले हैं, लेकिन प्रतिबिंब बनाने के लिए आपको भी यही करना होगा।
Bozho

जवाबों:


128

यह प्रदर्शित करना चाहिए कि इसे कैसे हल किया जाए:

import java.lang.reflect.Field;

class Super {
    private int i = 5;
}

public class B extends Super {
    public static void main(String[] args) throws Exception {
        B b = new B();
        Field f = b.getClass().getSuperclass().getDeclaredField("i");
        f.setAccessible(true);
        System.out.println(f.get(b));
    }
}

(या Class.getDeclaredFieldsसभी क्षेत्रों की एक सरणी के लिए।)

आउटपुट:

5

क्या यह सभी सुपरक्लास के क्षेत्र या सिर्फ प्रत्यक्ष सुपरक्लास प्राप्त करता है?
बारिश

प्रत्यक्ष सुपर क्लास के क्षेत्र। यदि आप उच्च स्तर पर जाना चाहते getSuperclass()हैं nullतो आप तब तक पुन: जमा कर सकते हैं।
शाम

आप अगले दो वक्तव्यों में सरणी एक्सेस का उपयोग getDeclaredFields()[0]या getDeclaredField("i")दोहराते क्यों नहीं हैं [0]?
होल्गर

यह इस विशेष प्रश्न को तैयार करने के तरीके के कारण है। यह मूल रूप से सिर्फ एक प्रदर्शन था कि कैसे उपयोग किया जाए getDeclaredFields। उत्तर अपडेट कर दिया गया है।
एरियोब

44

यहां सबसे अच्छा तरीका है विज़िटर पैटर्न का उपयोग करना जो क्लास और सभी सुपर क्लास में सभी फ़ील्ड्स को खोजता है और उन पर कॉलबैक एक्शन निष्पादित करता है।


कार्यान्वयन

स्प्रिंग में एक अच्छा उपयोगिता वर्ग है ReflectionUtilsजो बस इतना ही करता है: यह एक कॉलबैक के साथ सभी सुपर वर्गों के सभी क्षेत्रों पर लूप करने की एक विधि को परिभाषित करता है:ReflectionUtils.doWithFields()

प्रलेखन:

लक्ष्य वर्ग में सभी क्षेत्रों पर दिए गए कॉलबैक को आमंत्रित करें, सभी घोषित क्षेत्रों को प्राप्त करने के लिए वर्ग पदानुक्रम को बढ़ाएं।

पैरामीटर:
- क्लैज - विश्लेषण करने के लिए लक्ष्य वर्ग
- एफसी - प्रत्येक क्षेत्र के लिए आह्वान करने के लिए कॉलबैक
- एफएफ - वह फ़िल्टर जो कॉलबैक लागू करने के लिए खेतों को निर्धारित करता है

नमूना कोड:

ReflectionUtils.doWithFields(RoleUnresolvedList.class,
    new FieldCallback(){

        @Override
        public void doWith(final Field field) throws IllegalArgumentException,
            IllegalAccessException{

            System.out.println("Found field " + field + " in type "
                + field.getDeclaringClass());

        }
    },
    new FieldFilter(){

        @Override
        public boolean matches(final Field field){
            final int modifiers = field.getModifiers();
            // no static fields please
            return !Modifier.isStatic(modifiers);
        }
    });

आउटपुट:

पाया फ़ील्ड निजी क्षणिक बूलियन javax.management.relation.RoleUnresolvedList.typeSafe में टाइप क्लास javax.management.relation.RoleUnresolvedList
पाया गया फ़ील्ड निजी ट्रांजीशनल बूलियन javax.management.relation.oleUnresolvedList. टाइप क्लास javax.man.avession में
पाया गया। निजी क्षणिक java.lang.Object [] java.util.ArrayList.elementData में प्रकार वर्ग java.util.ArrayList
प्रकार वर्ग java.util.ArrayList में मिला क्षेत्र निजी पूर्णांक java.util.ArrayList.size
मिला क्षेत्र संरक्षित क्षणिक पूर्णांक जावा। use.AbstractList.modCount टाइप क्लास में java.util.AbstractList


3
यदि यह "विज़िटर पैटर्न" नहीं है, लेकिन यह अभी भी एक बहुत अच्छा उपकरण है यदि आपके पास अपने कोड में स्प्रिंग वायरस है। इसे साझा करने के लिए धन्यवाद :)
thinlizzy

2
@ jose.diego मुझे पूरा यकीन है कि आप इसके बारे में बहस कर सकते हैं। यह एक वस्तु पेड़ के बजाय एक वर्ग पदानुक्रम का दौरा करता है, लेकिन सिद्धांत एक ही रहता है
सीन पैट्रिक फ्लोयड

यकीन नहीं होता कि इस टिप्पणी पर प्रतिक्रिया मिलेगी, लेकिन आप इस समाधान के साथ एक समय में एक विशेष क्षेत्र का दौरा कर रहे हैं। अगर मुझे एक ही समय में अन्य क्षेत्रों को देखने की आवश्यकता है - उदाहरण के लिए, इस फ़ील्ड को "abc" पर सेट करें यदि कोई अन्य फ़ील्ड NULL है - मेरे पास संपूर्ण उपलब्ध वस्तु नहीं है।
जीन b।

इसका बहुत बुरा है कि इस वर्ग के लिए JavaDoc इंगित करता है कि "यह केवल आंतरिक उपयोग के लिए अभिप्रेत है", इसलिए यह जो भी इसका उपयोग करना चाहता है, उसके लिए यह एक संभावित जोखिम है।
अंतरिक्ष यात्री

1
@spacemanspiff आप तकनीकी रूप से सही हैं, लेकिन यह वर्ग लगभग 15 वर्षों से है (4 प्रमुख रिलीज़ संस्करण सहित) और व्यापक रूप से कई स्प्रिंग ग्राहकों द्वारा उपयोग किया गया है। मुझे संदेह है कि वे इसे अभी वापस खींच लेंगे।
शॉन पैट्रिक फ्लोयड

34

यह करूँगा:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        Collections.addAll(result, i.getDeclaredFields());
        i = i.getSuperclass();
    }

    return result;
}

यदि आप EclEmma जैसे कोड कवरेज टूल का उपयोग करते हैं , तो आपको यह देखना होगा: वे आपकी प्रत्येक कक्षा में एक छिपा हुआ क्षेत्र जोड़ते हैं। एक्लेम्मा के मामले में, इन क्षेत्रों को सिंथेटिक के रूप में चिह्नित किया जाता है , और आप उन्हें इस तरह से फ़िल्टर कर सकते हैं:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        for (Field field : i.getDeclaredFields()) {
            if (!field.isSynthetic()) {
                result.add(field);
            }
        }
        i = i.getSuperclass();
    }

    return result;
}

सिंथेटिक क्षेत्रों के बारे में आपकी टिप्पणी के लिए धन्यवाद, EMMA वही करता है।
अनातोली

यह तर्क वर्ग के घोषित और विरासत वाले क्षेत्र हो जाते हैं, इसलिए इसे getDeclaredAndInheritedPStreetFields नाम दिया जाना चाहिए। हालांकि धन्यवाद!
पीटर हॉकिन्स

1
isSynthetic पर अच्छी पकड़ :)
लुकास क्रॉफर्ड

शानदार उत्तर के लिए धन्यवाद ~
बारिश

19
public static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        try {
            Field f = tmpClass.getDeclaredField(fieldName);
            return f;
        } catch (NoSuchFieldException e) {
            tmpClass = tmpClass.getSuperclass();
        }
    } while (tmpClass != null);

    throw new RuntimeException("Field '" + fieldName
            + "' not found on class " + clazz);
}

( इस जवाब पर आधारित )


15

वास्तव में मैं एक जटिल प्रकार का उपयोग करता हूं ताकि आप समाधान पूरा न करें। मुझे सभी निजी विरासत वाले क्षेत्रों को प्राप्त करने के लिए एक पुनरावर्ती कॉल करने की आवश्यकता है। यहाँ मेरा समाधान है

 /**
 * Return the set of fields declared at all level of class hierachy
 */
public Vector<Field> getAllFields(Class clazz) {
    return getAllFieldsRec(clazz, new Vector<Field>());
}

private Vector<Field> getAllFieldsRec(Class clazz, Vector<Field> vector) {
    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        getAllFieldsRec(superClazz, vector);
    }
    vector.addAll(toVector(clazz.getDeclaredFields()));
    return vector;
}

हालाँकि, उसका समाधान आपको सही रास्ते पर मिला, है ना?
एपरकिंस

1
वेक्टर खराब पुराना कोड है। कृपया संग्रह ढांचे से एक मौजूदा डेटा संरचना का उपयोग करें (ArrayList ज्यादातर मामलों में पर्याप्त है)
शॉन पैट्रिक फ्लोयड

@ टपको का जवाब मेरी तरह दिखता है, लेकिन मैंने इसे पाया और फिर मैंने इसका जवाब देखा। @seanizer वेक्टर है कि पुराने नहीं है, और it'a संग्रह एपीआई के एक सदस्य
benzen

"जावा 2 प्लेटफॉर्म v1.2 के रूप में, इस वर्ग को सूची को लागू करने के लिए फिर से तैयार किया गया है, ताकि यह जावा के संग्रह लेआउट ढांचे का एक हिस्सा बन जाए।" 1.2 में रेट्रोफिटेड? अगर यह पुराना नहीं है तो क्या है? स्रोत: download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html
सीन पैट्रिक फ्लोयड

7
वेक्टर में एक विशाल ओवरहेड है क्योंकि सब कुछ सिंक्रनाइज़ है। और जहाँ आपको सिंक्रोनाइज़ेशन की आवश्यकता है, वहाँ java.util.concurrent में बेहतर कक्षाएं हैं। वेक्टर, हैशटेबल और स्ट्रिंगबफ़र को ज्यादातर मामलों में ArrayList, HashMap और StringBuilder द्वारा प्रतिस्थापित किया जाना चाहिए
सीन पैट्रिक फ्लोयड

8

मुझे मॉडल नागरिक में ब्लूप्रिंट के लिए विरासत वाले क्षेत्रों के लिए समर्थन जोड़ने की आवश्यकता थी । मैंने इस पद्धति को व्युत्पन्न किया है जो कि क्लास के खेतों + विरासत वाले क्षेत्रों को पुनः प्राप्त करने के लिए थोड़ा अधिक संक्षिप्त है।

private List<Field> getAllFields(Class clazz) {
    List<Field> fields = new ArrayList<Field>();

    fields.addAll(Arrays.asList(clazz.getDeclaredFields()));

    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        fields.addAll(getAllFields(superClazz));
    }

    return fields;
}

7
private static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        for ( Field field : tmpClass.getDeclaredFields() ) {
            String candidateName = field.getName();
            if ( ! candidateName.equals(fieldName) ) {
                continue;
            }
            field.setAccessible(true);
            return field;
        }
        tmpClass = tmpClass.getSuperclass();
    } while ( clazz != null );
    throw new RuntimeException("Field '" + fieldName +
        "' not found on class " + clazz);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.