जावा में एक स्थिर विधि से वर्ग का नाम प्राप्त करना


244

उस कक्षा में स्थिर विधि से वर्ग का नाम कैसे प्राप्त किया जा सकता है। उदाहरण के लिए

public class MyClass {
    public static String getClassName() {
        String name = ????; // what goes here so the string "MyClass" is returned
        return name;
    }
}

इसे संदर्भ में रखने के लिए, मैं वास्तव में एक अपवाद के रूप में एक संदेश के भाग के रूप में वर्ग का नाम वापस करना चाहता हूं।


try{ throw new RuntimeEsception();} catch(RuntimeEcxeption e){return e.getstackTrace()[1].getClassName();}
अरमिनवनबुरीन

जवाबों:


231

सही तरीके से रीफैक्टरिंग का समर्थन करने के लिए (नाम बदलें वर्ग), तो आपको या तो उपयोग करना चाहिए:

 MyClass.class.getName(); // full name with package

या ( @James Van Huis के लिए धन्यवाद ):

 MyClass.class.getSimpleName(); // class name and no more

136
यदि आप MyClass के ज्ञान में हार्ड-कोड पर जा रहे हैं, तो आप स्ट्रिंग नाम = "MyClass" भी कर सकते हैं; !
जॉन टॉपले

111
लेकिन तब, आपके आईडीई में वर्ग का नाम वापस लेने से काम ठीक से नहीं होगा।
जेम्स वान हुआस

9
सच। हालांकि MyClass.class यह सुनिश्चित करेगा कि यह लाइन 'परिवर्तन श्रेणी के नाम' के साथ भूल न जाए
उपकरणपट्टी

19
मैं करना चाहते हैं "इस" एक स्थिर संदर्भ में काम किया जावा में वर्तमान कक्षा मतलब करने के लिए, कि है कि "class.xxx" इस वर्ग मतलब करने के लिए या तो उदाहरण या स्थिर कोड में अनुमति दी गई थी! इसके साथ समस्या यह है कि MyClass क्रिया और निरर्थक है, संदर्भ में। लेकिन फिर मैं जावा को जितना पसंद करता हूं, यह क्रियात्मकता की ओर झुकाव करता है।
लॉरेंस डोल

51
क्या होगा यदि मैं एक उपवर्ग में स्थैतिक विधि कह रहा हूं, और मुझे उपवर्ग नाम चाहिए?
एडवर्ड फॉक

120

टूलकिट क्या कहते हैं। ऐसा कुछ न करें:

return new Object() { }.getClass().getEnclosingClass();

7
यदि क्लास एक और बढ़ाता है, तो यह वास्तविक क्लास, केवल बेस क्लास को वापस नहीं करता है।
लुइस सोइरो

1
@LuisSoeiro का मानना ​​है कि यह उस वर्ग को लौटाता है जिसे विधि में परिभाषित किया गया है। मुझे यकीन नहीं है कि आधार वर्ग कारक स्थिर संदर्भ में कैसे हैं।
टॉम हैटिन -

8
मुझे समझ में नहीं आता है कि getClass () स्थिर क्यों नहीं हो सकता है। इस "मुहावरे" की जरूरत नहीं होगी।
mmirwaldt

1
@mmirwaldt getClassरनटाइम प्रकार लौटाता है, इसलिए स्थिर नहीं हो सकता।
टॉम हॉकिन -

5
यही असली जवाब है। क्योंकि आपको अपने कोड में अपनी कक्षा का नाम स्वयं लिखने की आवश्यकता नहीं है।
बोब्स

99

जावा 7+ में आप इसे स्टैटिक विधि / क्षेत्र में कर सकते हैं:

MethodHandles.lookup().lookupClass()

मैं कहने वाला था Reflection.getCallerClass()। लेकिन यह 'सूरज' पैकेज में होने के बारे में एक चेतावनी देता है। तो यह एक बेहतर समाधान हो सकता है।
फॉम्पी

1
@ फ़ॉम्पी: जावा 9 एक आधिकारिक एपीआई पेश करने जा रहा है जो इस अनौपचारिक Reflection.getCallerClass()चीज़ को सुपरसेड करेगा । यह उनके तुच्छ संचालन के लिए थोड़ा जटिल है, अर्थात Optional<Class<?>> myself = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .walk(s -> s.map(StackWalker.StackFrame::getDeclaringClass) .findFirst());, लेकिन निश्चित रूप से, यह इस तथ्य से जुड़ा है कि यह अधिक शक्तिशाली होगा।
होल्गर

6
यह आसानी से सबसे अच्छा उपाय है। यह वास्तविक वर्ग के नाम को निर्दिष्ट करने की आवश्यकता से बचता है, यह अस्पष्ट नहीं है, यह हैक नहीं है और इसके नीचे Artyom Krivolapov की पोस्ट के अनुसार यह सबसे तेज़ दृष्टिकोण भी है।
स्कोमिसा

@Rein अगर रन बेस क्लास को आधार वर्ग में कहा जाता है तो क्या कोई रास्ता है?
ग्लाइड

59

इसलिए, हमारे पास एक ऐसी स्थिति है जब हमें MyClass.classसिंटैक्स के स्पष्ट उपयोग के बिना सांख्यिकीय रूप से वर्ग वस्तु या एक वर्ग पूर्ण / सरल नाम प्राप्त करने की आवश्यकता होती है ।

यह वास्तव में कुछ मामलों में आसान हो सकता है, उदाहरण के लिए लकड़हारा उदाहरण ऊपरी-स्तरीय फ़ंक्शन (इस मामले में कोटलिन एक स्थिर जावा वर्ग बनाता है जो कोटलिन कोड से सुलभ नहीं है)।

इस जानकारी को प्राप्त करने के लिए हमारे पास कुछ भिन्न प्रकार हैं:

  1. new Object(){}.getClass().getEnclosingClass();
    टॉम हॉकिन ने उल्लेख किया है - डीललाइन

  2. getClassContext()[0].getName();से SecurityManager
    द्वारा नोट क्रिस्टोफ़र

  3. new Throwable().getStackTrace()[0].getClassName();
    गिनती के द्वारा

  4. Thread.currentThread().getStackTrace()[1].getClassName();
    से Keksi

  5. और अंत में रेन
    MethodHandles.lookup().lookupClass();
    से कमाल


मैंने तैयार किया है सभी प्रकारों और परिणामों के लिए बेंचमार्क हैं:

# Run complete. Total time: 00:04:18

Benchmark                                                      Mode  Cnt      Score     Error  Units
StaticClassLookup.MethodHandles_lookup_lookupClass             avgt   30      3.630 ±   0.024  ns/op
StaticClassLookup.AnonymousObject_getClass_enclosingClass      avgt   30    282.486 ±   1.980  ns/op
StaticClassLookup.SecurityManager_classContext_1               avgt   30    680.385 ±  21.665  ns/op
StaticClassLookup.Thread_currentThread_stackTrace_1_className  avgt   30  11179.460 ± 286.293  ns/op
StaticClassLookup.Throwable_stackTrace_0_className             avgt   30  10221.209 ± 176.847  ns/op


निष्कर्ष

  1. उपयोग करने के लिए सबसे अच्छा संस्करण , बल्कि साफ और राक्षसी रूप से तेज।
    केवल जावा 7 और एंड्रॉइड एपीआई 26 के बाद से उपलब्ध है!
 MethodHandles.lookup().lookupClass();
  1. यदि आपको एंड्रॉइड या जावा 6 के लिए इस कार्यक्षमता की आवश्यकता है, तो आप दूसरे सर्वश्रेष्ठ संस्करण का उपयोग कर सकते हैं। यह बहुत तेज़ है, लेकिन उपयोग के प्रत्येक स्थान में एक अनाम वर्ग बनाता है :(
 new Object(){}.getClass().getEnclosingClass();
  1. यदि आपको कई स्थानों पर इसकी आवश्यकता है और नहीं चाहते हैं कि आपका बाइटकोड गुमनाम वर्गों के टन के कारण फूल जाए - SecurityManagerआपका मित्र है (तीसरा सबसे अच्छा विकल्प)।

    लेकिन आप सिर्फ कॉल नहीं कर सकते getClassContext()- यह SecurityManagerकक्षा में संरक्षित है । आपको कुछ सहायक वर्ग की आवश्यकता होगी:

 // Helper class
 public final class CallerClassGetter extends SecurityManager
 {
    private static final CallerClassGetter INSTANCE = new CallerClassGetter();
    private CallerClassGetter() {}

    public static Class<?> getCallerClass() {
        return INSTANCE.getClassContext()[1];
    }
 }

 // Usage example:
 class FooBar
 {
    static final Logger LOGGER = LoggerFactory.getLogger(CallerClassGetter.getCallerClass())
 }
  1. संभवतः आपको getStackTrace()अपवाद या के आधार पर अंतिम दो वेरिएंट का उपयोग करने की आवश्यकता नहीं है Thread.currentThread()बहुत अकुशल और केवल वर्ग के नाम को एक के रूप में वापस कर सकता है String, Class<*>उदाहरण के लिए नहीं ।


पुनश्च

यदि आप स्थैतिक कोटलीन बर्तनों के लिए एक लकड़हारा उदाहरण बनाना चाहते हैं (जैसे :), आप इस सहायक का उपयोग कर सकते हैं:

import org.slf4j.Logger
import org.slf4j.LoggerFactory

// Should be inlined to get an actual class instead of the one where this helper declared
// Will work only since Java 7 and Android API 26!
@Suppress("NOTHING_TO_INLINE")
inline fun loggerFactoryStatic(): Logger
    = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass())

उपयोग उदाहरण:

private val LOGGER = loggerFactoryStatic()

/**
 * Returns a pseudo-random, uniformly distributed value between the
 * given least value (inclusive) and bound (exclusive).
 *
 * @param min the least value returned
 * @param max the upper bound (exclusive)
 *
 * @return the next value
 * @throws IllegalArgumentException if least greater than or equal to bound
 * @see java.util.concurrent.ThreadLocalRandom.nextDouble(double, double)
 */
fun Random.nextDouble(min: Double = .0, max: Double = 1.0): Double {
    if (min >= max) {
        if (min == max) return max
        LOGGER.warn("nextDouble: min $min > max $max")
        return min
    }
    return nextDouble() * (max - min) + min
}

38

यह निर्देश ठीक काम करता है:

Thread.currentThread().getStackTrace()[1].getClassName();

5
ध्यान रखना, कि यह वास्तव में धीमा हो सकता है। हालाँकि आप इसे कॉपी पेस्ट कर सकते हैं।
गैबोर लिप्टेक

1
प्रत्येक बार इसका उपयोग करने पर ऑब्जेक्ट या थ्रेड नहीं बनाने का अतिरिक्त लाभ होता है।
एलिग सेगल-हलेवी

5
@ErelSegalHalevi यह की एक पूरी बहुत बनाता है StackTraceElement पृष्ठभूमि हालांकि :( में रों
नवीन

4
यदि आप स्रोत कोड को Thread.getStackTrace()देखते हैं तो आप देखेंगे कि इसे return (new Exception()).getStackTrace();कॉल किए जाने के मामले में और कुछ नहीं करता है currentThread()। तो @count ludwig का समाधान उसी को प्राप्त करने का अधिक सीधा तरीका है।
टी-बुल

34

आप इस तरह से जेएनआई का उपयोग करके वास्तव में कुछ मीठा कर सकते हैं:

MyObject.java:

public class MyObject
{
    static
    {
        System.loadLibrary( "classname" );
    }

    public static native String getClassName();

    public static void main( String[] args )
    {
        System.out.println( getClassName() );
    }
}

फिर:

javac MyObject.java
javah -jni MyObject

फिर:

MyObject.c:

#include "MyObject.h"

JNIEXPORT jstring JNICALL Java_MyObject_getClassName( JNIEnv *env, jclass cls )
{
    jclass javaLangClass = (*env)->FindClass( env, "java/lang/Class" );
    jmethodID getName = (*env)->GetMethodID( env, javaLangClass, "getName",
        "()Ljava/lang/String;" );
    return (*env)->CallObjectMethod( env, cls, getName );
}

फिर C को साझा लाइब्रेरी में संकलित करें libclassname.soऔर जावा चलाएं!

* व्यंग्य


यह क्यों नहीं बनाया गया है?
ggb667

चतुर, और मैं हास्य की सराहना करता हूं। मरहम में मक्खी यह है कि सी फ़ंक्शन Java_MyObject_getClassNameका डिफ़ॉल्ट नाम एम्बेडेड नाम है। जिस तरह से चारों ओर जेएनआई का उपयोग करना है RegisterNatives। बेशक आपको जेएनआई के साथ खिलाना होगा FindClass(env, 'com/example/MyObject'), इसलिए वहां कोई भी जीत नहीं होगी।
Renate

2
@ फिर से बताएं, तो यह पूरा जवाब वास्तव में एक मजाक है? यदि हां, तो कृपया इसे बहुत स्पष्ट करें क्योंकि आप जानते हैं, हम अन्य लोगों की मदद करने वाले हैं, इसलिए चलिए निर्दोषों को जाल में न फँसाएँ।
स्टीफन गौरिचोन

खैर, यह मेरा मजाक नहीं था और मैंने बताया कि यह एक मजाक था। इस परिदृश्य के लिए उपयोग मामला आमतौर पर लॉग में वर्ग की पहचान करने के लिए होता है। सभी जटिलता को दूर करते हुए, यह आमतौर पर private static final String TAG = "MyClass"या तो करने के लिए उबलता है: या private static final String TAG = MyClass.class.getSimpleName();दूसरा आईडीई का उपयोग करके वैश्विक वर्ग का नाम बदलने के लिए अधिक अनुकूल है।
Renate

20

मैं अपनी कक्षाओं (या एनोटेट) के शीर्ष पर Log4j लॉगर इनिट का उपयोग करता हूं।

PRO: थ्रोएबल पहले से ही लोड है और आप "IO भारी" SecurityManager का उपयोग न करके संसाधनों को बचा सकते हैं।

कांग्रेस: ​​कुछ सवाल के रूप में यह सभी JVMs के लिए काम करेगा।

// Log4j . Logger --- Get class name in static context by creating an anonymous Throwable and 
// getting the top of its stack-trace. 
// NOTE you must use: getClassName() because getClass() just returns StackTraceElement.class 
static final Logger logger = Logger.getLogger(new Throwable() .getStackTrace()[0].getClassName()); 

अपना स्वयं का अपवाद वर्ग बनाएं ताकि jvm अभ्यस्त परेशान न हों: jhocr.googlecode.com/svn/trunk/src/main/java/com/googlecode/…
4F2E4A2E

1
यदि आप इस समाधान के रूप में भयानक के रूप में कुछ सुझाव देने जा रहे हैं, तो कृपया कम से कम सुरक्षा पेशेवरों का दुरुपयोग करने जैसे एक और भयानक समाधान के बजाय MyClass.class.getName () का उपयोग करने के प्राकृतिक समाधान से अपने पेशेवरों से संबंधित करें।
सोरेन बोइसन

अधिक विपक्ष: बहुत क्रिया; धीमा (यह वास्तव में एक मामूली बिंदु है क्योंकि यह केवल एक बार चलता है, जब कक्षा लोड होती है)।
टूलबार

13

सुरक्षा प्रबंधक का दुरुपयोग करें

System.getSecurityManager().getClassContext()[0].getName();

या, यदि सेट नहीं किया गया है, तो एक आंतरिक वर्ग का उपयोग करें जो इसे बढ़ाता है (उदाहरण के लिए रियल के HowTo से कॉपी किया गया शर्मनाक ):

public static class CurrentClassGetter extends SecurityManager {
    public String getClassName() {
        return getClassContext()[1].getName(); 
    }
}

9

यदि आप इसके साथ पूरे पैकेज का नाम चाहते हैं, तो कॉल करें:

String name = MyClass.class.getCanonicalName();

यदि आप केवल अंतिम तत्व चाहते हैं, तो कॉल करें:

String name = MyClass.class.getSimpleName();

5

कॉलगर्ल की श्रेणी का शब्दशः उपयोग MyClass.class.getName()वास्तव में काम करता है, लेकिन यदि आप इस कोड को कई वर्गों / उपवर्गों में प्रचारित करते हैं, जहां आपको इस वर्ग के नाम की आवश्यकता है, तो कॉपी / पेस्ट त्रुटियों का खतरा है।

और टॉम हैटिन का नुस्खा वास्तव में बुरा नहीं है, बस इसे सही तरीके से पकाने की जरूरत है :)

यदि आपके पास स्थैतिक विधि के साथ एक आधार वर्ग है जिसे उपवर्गों से बुलाया जा सकता है, और इस स्थैतिक विधि को वास्तविक कॉलर की कक्षा को जानने की आवश्यकता है, तो इसे निम्न की तरह प्राप्त किया जा सकता है:

class BaseClass {
  static sharedStaticMethod (String callerClassName, Object... otherArgs) {
    useCallerClassNameAsYouWish (callerClassName);
    // and direct use of 'new Object() { }.getClass().getEnclosingClass().getName()'
    // instead of 'callerClassName' is not going to help here,
    // as it returns "BaseClass"
  }
}

class SubClass1 extends BaseClass {
  static someSubclassStaticMethod () {
    // this call of the shared method is prone to copy/paste errors
    sharedStaticMethod (SubClass1.class.getName(),
                        other_arguments);
    // and this call is safe to copy/paste
    sharedStaticMethod (new Object() { }.getClass().getEnclosingClass().getName(),
                        other_arguments);
  }
}

4

एक refactoring- सुरक्षित, कट और पेस्ट-सुरक्षित समाधान जो नीचे दिए गए तदर्थ वर्गों की परिभाषा से बचता है।

एक स्थिर विधि लिखें जो वर्ग नाम को विधि नाम में शामिल करने के लिए देखभाल करने वाले वर्ग नाम को पुनर्प्राप्त करता है:

private static String getMyClassName(){
  return MyClass.class.getName();
}

फिर इसे अपनी स्थिर विधि में याद करें:

public static void myMethod(){
  Tracer.debug(getMyClassName(), "message");
}

स्ट्रैकटिंग सुरक्षा का उपयोग स्ट्रिंग्स के उपयोग से बचने के द्वारा किया जाता है, कट और पेस्ट सुरक्षा दी जाती है क्योंकि यदि आप कॉलर विधि को काटते और पेस्ट करते हैं तो आपको getMyClassName () लक्ष्य "MyClass2" क्लास में नहीं मिलेगा, इसलिए आपको इसे फिर से परिभाषित करने और इसे अपडेट करने के लिए मजबूर किया जाएगा।


3

सवाल के बजाय `ClassName.class` के बजाय` this.class` जैसा कुछ? को इस के लिए एक डुप्लिकेट के रूप में चिह्नित किया जाता है (जो कि तर्कपूर्ण है क्योंकि यह प्रश्न कक्षा के नाम के बजाय कक्षा के बारे में है), मैं यहां उत्तर पोस्ट कर रहा हूं:

class MyService {
    private static Class thisClass = MyService.class;
    // or:
    //private static Class thisClass = new Object() { }.getClass().getEnclosingClass();
    ...
    static void startService(Context context) {
        Intent i = new Intent(context, thisClass);
        context.startService(i);
    }
}

निजी केthisClass रूप में परिभाषित करना महत्वपूर्ण है क्योंकि: 1) यह विरासत में नहीं होना चाहिए: व्युत्पन्न वर्ग को या तो अपने स्वयं को परिभाषित करना चाहिए या त्रुटि संदेश 2 का उत्पादन करना चाहिए) अन्य वर्गों से संदर्भ के बजाय किया जाना चाहिए ।
thisClass
ClassName.classClassName.thisClass

thisClassपरिभाषित के साथ , वर्ग नाम तक पहुंच बन जाती है:

thisClass.getName()

1

मुझे कई वर्गों के स्थिर तरीकों में वर्ग नाम की आवश्यकता थी इसलिए मैंने निम्नलिखित विधि के साथ एक JavaUtil क्लास लागू किया:

public static String getClassName() {
    String className = Thread.currentThread().getStackTrace()[2].getClassName();
    int lastIndex = className.lastIndexOf('.');
    return className.substring(lastIndex + 1);
}

आशा है कि यह मदद करेगा!


1
न केवल मैजिक नंबर 2 (कि आसानी से NullPointerException में परिणाम हो सकता है) के कारण इसका उपयोग करना बुरा है, लेकिन आप वर्चुअल मशीन की सटीकता पर भरोसा कर रहे हैं। विधि के javadoc से: * कुछ आभासी मशीनें, कुछ परिस्थितियों में, स्टैक ट्रेस से एक या अधिक स्टैक फ़्रेम को छोड़ सकती हैं। चरम मामले में, एक आभासी मशीन जिसमें इस थ्रेड के बारे में कोई स्टैक ट्रेस जानकारी नहीं है, इस पद्धति से शून्य-लंबाई सरणी वापस करने की अनुमति है। *
शॉटगन

0

मैंने इन दोनों दृष्टिकोणों staticऔर non staticपरिदृश्य के लिए उपयोग किया है:

मुख्य वर्ग:

//For non static approach
public AndroidLogger(Object classObject) {
    mClassName = classObject.getClass().getSimpleName();
}

//For static approach
public AndroidLogger(String className) {
    mClassName = className;
}

वर्ग नाम कैसे प्रदान करें:

गैर स्थिर तरीका:

private AndroidLogger mLogger = new AndroidLogger(this);

स्थिर तरीका:

private static AndroidLogger mLogger = new AndroidLogger(Myclass.class.getSimpleName());

-1

यदि आप प्रतिबिंब का उपयोग कर रहे हैं, तो आप मेथड ऑब्जेक्ट प्राप्त कर सकते हैं और फिर:

method.getDeclaringClass().getName()

स्वयं विधि प्राप्त करने के लिए, आप संभवतः इसका उपयोग कर सकते हैं:

Class<?> c = Class.forName("class name");
Method  method = c.getDeclaredMethod ("method name", parameterTypes)

3
और आप कैसे जानेंगे कि क्या है: "वर्ग का नाम"? :)
अल्फासिन

1
बीत रहा है Class.forName("class name")पहले से ही आप एक वर्ग दे। आप इसे विधि के माध्यम से पुनः प्राप्त क्यों करना चाहते हैं?
सेर्गेई आइरिसोव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.