जावा प्रतिबिंब: एक चर का नाम कैसे प्राप्त करें?


139

जावा प्रतिबिंब का उपयोग करना, क्या स्थानीय चर का नाम प्राप्त करना संभव है? उदाहरण के लिए, यदि मेरे पास यह है:

Foo b = new Foo();
Foo a = new Foo();
Foo r = new Foo();

क्या ऐसा तरीका लागू करना संभव है जो उन चरों के नाम पा सके, जैसे:

public void baz(Foo... foos)
{
    for (Foo foo: foos) {
        // Print the name of each foo - b, a, and r
        System.out.println(***); 
    }
}

संपादित करें: यह प्रश्न भिन्न है कि क्या जावा में एक चर का नाम खोजने का कोई तरीका है जो किसी फ़ंक्शन को दिया गया था? इसमें यह अधिक विशुद्ध रूप से सवाल पूछता है कि क्या कोई स्थानीय चर का नाम निर्धारित करने के लिए प्रतिबिंब का उपयोग कर सकता है, जबकि अन्य प्रश्न (स्वीकृत उत्तर सहित) चर के परीक्षण मूल्यों पर अधिक केंद्रित है।


11
सभी शानदार जवाब! सभी को जवाब देने और टिप्पणी करने के लिए धन्यवाद - यह एक दिलचस्प और व्यावहारिक चर्चा रही है।
डेविड कोएले


हो सकता है। मेरी [देख लो] [१]। JDK 1.1 से JDK 7. [1] के लिए काम करता है: gist.github.com/2011728
वेंडल चेन


3
डुप्लिकेट नहीं, और मेरे प्रश्न को अपडेट करने के लिए कि क्यों। यदि कुछ भी हो, तो वह अन्य प्रश्न इस एक का डुप्लिकेट (या विशेष मामला) है!
डेविड कोएले

जवाबों:


65

जावा 8 के अनुसार, कुछ स्थानीय चर नाम जानकारी प्रतिबिंब के माध्यम से उपलब्ध है। देखें "अपडेट" अनुभाग देखें।

पूरी जानकारी अक्सर वर्ग फ़ाइलों में संग्रहीत होती है। एक संकलन समय अनुकूलन इसे हटाने के लिए है, अंतरिक्ष की बचत और (कुछ अवलोकन प्रदान करने के लिए)। हालांकि, जब यह मौजूद होता है, तो प्रत्येक विधि में एक स्थानीय चर तालिका विशेषता होती है जो स्थानीय चर के प्रकार और नाम को सूचीबद्ध करती है, और निर्देशों की श्रेणी जहां वे दायरे में हैं।

शायद ASM जैसी बाइट-कोड इंजीनियरिंग लाइब्रेरी आपको रनटाइम पर इस जानकारी का निरीक्षण करने की अनुमति देगी। इस जानकारी की आवश्यकता के लिए एकमात्र उचित स्थान जो मैं सोच सकता हूं, वह एक विकास उपकरण में है, और इसलिए बाइट-कोड इंजीनियरिंग अन्य उद्देश्यों के लिए भी उपयोगी होने की संभावना है।


अद्यतन: इसके लिए सीमित समर्थन को जावा 8 में जोड़ा गया था पैरामीटर (स्थानीय चर का एक विशेष वर्ग) नाम अब प्रतिबिंब के माध्यम से उपलब्ध हैं। अन्य उद्देश्यों के बीच, यह @ParameterNameनिर्भरता इंजेक्शन कंटेनरों द्वारा उपयोग किए जाने वाले एनोटेशन को बदलने में मदद कर सकता है ।


49

यह बिल्कुल भी संभव नहीं है। जावा के भीतर परिवर्तनीय नामों का संचार नहीं किया जाता है (और संकलक अनुकूलन के कारण भी हटाया जा सकता है)।

EDIT (टिप्पणियों से संबंधित):

यदि आप इसे फ़ंक्शन मापदंडों के रूप में उपयोग करने के विचार से पीछे हटते हैं, तो यहां एक विकल्प है (जिसका मैं उपयोग नहीं करूंगा - नीचे देखें):

public void printFieldNames(Object obj, Foo... foos) {
    List<Foo> fooList = Arrays.asList(foos);
    for(Field field : obj.getClass().getFields()) {
         if(fooList.contains(field.get()) {
              System.out.println(field.getName());
         }
    }
}

ऐसे मुद्दे होंगे यदि a == b, a == r, or b == rअन्य क्षेत्र हैं जिनके संदर्भ समान हैं।

प्रश्न स्पष्ट होने के बाद अब EDIT अनावश्यक है


फिर आप इसे कैसे समझाते हैं: java.sun.com/javase/6/docs/api/java/lang/reflect/Field.html ?
आउटलॉ प्रोग्रामर

1
-1: मुझे लगता है कि आपने गलत समझा है। @ डेविड खेतों के बाद है, स्थानीय चर नहीं। प्रतिबिंब चर के माध्यम से स्थानीय चर वास्तव में अनुपलब्ध हैं।
ल्यूक वुडवर्ड

मुझे लगता है कि Pourquoi Litytestdata सही है। जाहिर है कि खेतों को दूर नहीं किया जा सकता है, इसलिए मार्सेल जे को स्थानीय चर के बारे में सोचना चाहिए।
माइकल मायर्स

3
@ डेविड: आपको यह स्पष्ट करने के लिए संपादित करना होगा कि आपके पास स्थानीय चर के बजाय फ़ील्ड का मतलब है। मूल प्रश्न कोड देता है जो स्थानीय चरों के रूप में b, a और r को घोषित करता है।
जेसन एस

7
मेरा मतलब था स्थानीय चर, और मैंने उस प्रश्न को संपादित करने के लिए संपादित किया है। मुझे लगा कि चर नामों को प्राप्त करना संभव नहीं हो सकता है, लेकिन मुझे लगा कि यह असंभव मानने से पहले मैं एसओ से पूछूंगा।
डेविड कोएले

30

( संपादित करें: पिछले दो उत्तर हटा दिए गए, एक प्रश्न के उत्तर के लिए, क्योंकि यह संपादन से पहले खड़ा था और एक होने के लिए, यदि बिल्कुल गलत नहीं है, तो कम से कम इसके करीब। )

यदि आप डिबग जानकारी ( javac -g) पर संकलित करते हैं , तो स्थानीय चर का नाम .class फ़ाइल में रखा जाता है। उदाहरण के लिए, इस सरल वर्ग को लें:

class TestLocalVarNames {
    public String aMethod(int arg) {
        String local1 = "a string";
        StringBuilder local2 = new StringBuilder();
        return local2.append(local1).append(arg).toString();
    }
}

संकलन करने के बाद javac -g:vars TestLocalVarNames.java, स्थानीय चर का नाम अब .class फ़ाइल में है। javapका -lझंडा ("प्रिंट लाइन नंबर और स्थानीय चर तालिकाएं") उन्हें दिखा सकता है।

javap -l -c TestLocalVarNames दिखाता है:

class TestLocalVarNames extends java.lang.Object{
TestLocalVarNames();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      5      0    this       LTestLocalVarNames;

public java.lang.String aMethod(int);
  Code:
   0:   ldc     #2; //String a string
   2:   astore_2
   3:   new     #3; //class java/lang/StringBuilder
   6:   dup
   7:   invokespecial   #4; //Method java/lang/StringBuilder."<init>":()V
   10:  astore_3
   11:  aload_3
   12:  aload_2
   13:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   16:  iload_1
   17:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   20:  invokevirtual   #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   23:  areturn

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      24      0    this       LTestLocalVarNames;
   0      24      1    arg       I
   3      21      2    local1       Ljava/lang/String;
   11      13      3    local2       Ljava/lang/StringBuilder;
}

वीएम कल्पना बताते हैं कि हम यहाँ क्या देख रहे हैं:

§4.7.9 LocalVariableTableगुण :

LocalVariableTableविशेषता एक का एक वैकल्पिक चर लंबाई विशेषता है Code(§4.7.3) विशेषता। किसी विधि के निष्पादन के दौरान किसी दिए गए स्थानीय चर का मान निर्धारित करने के लिए डीबगर्स द्वारा इसका उपयोग किया जा सकता है।

LocalVariableTableस्टोर के नामों और प्रत्येक स्लॉट में चर के प्रकार, तो यह बाईटकोड के साथ उन्हें मैच के लिए संभव है। यह है कि डिबगर "अभिव्यक्ति का मूल्यांकन" कैसे कर सकते हैं।

जैसा कि एरिकसन ने कहा, हालांकि, सामान्य प्रतिबिंब के माध्यम से इस तालिका तक पहुंचने का कोई तरीका नहीं है। यदि आप अभी भी ऐसा करने के लिए दृढ़ हैं, मेरा मानना ​​है कि जावा प्लेटफ़ॉर्म डीबगर आर्किटेक्चर (JPDA) मदद करेगा (लेकिन मैंने इसे स्वयं कभी इस्तेमाल नहीं किया है)।


1
जब मैं संपादन कर रहा था, उह-इक्केसन ने पोस्ट किया और अब मैं उसका विरोध कर रहा हूं। जिसका मतलब है कि मैं गलत हूं।
माइकल मायर्स

डिफ़ॉल्ट रूप से, javacडिबगिंग की सहायता के लिए प्रत्येक विधि के लिए कक्षा में एक स्थानीय चर तालिका डालता है। स्थानीय चर तालिका देखने के लिए -lविकल्प का उपयोग करें javap
इरिकसन

डिफ़ॉल्ट रूप से नहीं, ऐसा लगता है। मुझे javac -g:varsइसे प्राप्त करने के लिए उपयोग करना था । (मैं संपादित करने के लिए पिछले तीन घंटे के लिए इस उत्तर की कोशिश कर रहा गया है, लेकिन जैसा कि मैंने कहा मेरी नेटवर्क कनेक्शन समस्या हो रही है, जो यह अनुसंधान करने के लिए कठिन बना देता है।)
माइकल मायर्स

2
आप सही हैं, इस बारे में क्षमा करें। यह पंक्ति संख्याएं हैं जो डिफ़ॉल्ट रूप से "चालू" हैं।
इरिकसन

15
import java.lang.reflect.Field;


public class test {

 public int i = 5;

 public Integer test = 5;

 public String omghi = "der";

 public static String testStatic = "THIS IS STATIC";

 public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
  test t = new test();
  for(Field f : t.getClass().getFields()) {
   System.out.println(f.getGenericType() +" "+f.getName() + " = " + f.get(t));
  }
 }

}

3
getDeclaredFields()यदि आप निजी क्षेत्र के नाम चाहते हैं तो इसका उपयोग किया जा सकता है
coffeMug

10

आप इस तरह कर सकते हैं:

Field[] fields = YourClass.class.getDeclaredFields();
//gives no of fields
System.out.println(fields.length);         
for (Field field : fields) {
    //gives the names of the fields
    System.out.println(field.getName());   
}

आपका जवाब सभी फिड को प्राप्त करने के लिए ठीक काम करता है। केवल एक फ़ील्ड प्राप्त करने के लिए, जब मैं उपयोग करता हूं: YourClass.class.getDeclaredField ("फ़ील्ड 1"); मुझे NullPointer मिलता है। इसका उपयोग करने में क्या समस्या है? मुझे getDeclaredField विधि का उपयोग कैसे करना चाहिए?
शशि रंजन 4

0

आपको बस एक फ़ील्ड बनाने की ज़रूरत है और फिर इसे नीचे दिखाए गए वर्ग की तरह सेट करें।

Field fld[] = (class name).class.getDeclaredFields();   
for(Field x : fld)
{System.out.println(x);}

उदाहरण के लिए यदि आपने किया

Field fld[] = Integer.class.getDeclaredFields();
          for(Field x : fld)
          {System.out.println(x);}

आपको मिलेगा

public static final int java.lang.Integer.MIN_VALUE
public static final int java.lang.Integer.MAX_VALUE
public static final java.lang.Class java.lang.Integer.TYPE
static final char[] java.lang.Integer.digits
static final char[] java.lang.Integer.DigitTens
static final char[] java.lang.Integer.DigitOnes
static final int[] java.lang.Integer.sizeTable
private static java.lang.String java.lang.Integer.integerCacheHighPropValue
private final int java.lang.Integer.value
public static final int java.lang.Integer.SIZE
private static final long java.lang.Integer.serialVersionUID

0

सामान्य रूप से @Marcel Jackwerth का उत्तर अपडेट करें।

और केवल वर्ग विशेषता के साथ काम करना, विधि चर के साथ काम नहीं करना।

    /**
     * get variable name as string
     * only work with class attributes
     * not work with method variable
     *
     * @param headClass variable name space
     * @param vars      object variable
     * @throws IllegalAccessException
     */
    public static void printFieldNames(Object headClass, Object... vars) throws IllegalAccessException {
        List<Object> fooList = Arrays.asList(vars);
        for (Field field : headClass.getClass().getFields()) {
            if (fooList.contains(field.get(headClass))) {
                System.out.println(field.getGenericType() + " " + field.getName() + " = " + field.get(headClass));
            }
        }
    }

-1

इस उदाहरण को देखें:

PersonneTest pt=new PersonneTest();
System.out.println(pt.getClass().getDeclaredFields().length);
Field[]x=pt.getClass().getDeclaredFields();
System.out.println(x[1].getName());
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.