Sınıf adını Java'da statik bir yöntemden alma


244

Bu sınıftaki statik yöntemden sınıfın adı nasıl alınabilir. Örneğin

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

Bağlam koymak için, aslında bir istisna bir mesajın bir parçası olarak sınıf adını döndürmek istiyorum.


try{ throw new RuntimeEsception();} catch(RuntimeEcxeption e){return e.getstackTrace()[1].getClassName();}
arminvanbuuren

Yanıtlar:


231

Yeniden düzenlemeyi doğru bir şekilde desteklemek için (sınıfı yeniden adlandırın), aşağıdakilerden birini kullanmalısınız:

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

veya ( @James Van Huis sayesinde ):

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

136
Böyle MyClass bilgisinde sabit kod kullanacaksanız, sadece String name = "MyClass" da yapabilirsiniz; !
John Topley

111
Ancak, IDE'nizdeki sınıf adını yeniden düzenlemek düzgün çalışmaz.
James Van Huis

9
Doğru. Her ne kadar MyClass.class bu satırın 'sınıf adını değiştir' yeniden düzenleme ile unutulmamasını sağlayacak olsa da
toolkit

19
Ben dilek "Bu" o "class.xxx" bu sınıfı anlamında ya örneği veya statik kod izin verildi, Java geçerli Class anlamında statik bir şekilde çalıştı! Buradaki sorun, MyClass'ın bağlamda ayrıntılı ve gereksiz olmasıdır. Ama sonra Java'yı sevdiğim kadar ayrıntılara doğru eğilmiş gibi görünüyor.
Lawrence Dol

51
Bir alt sınıfta statik yöntemi çağırıyor ve alt sınıf adını istiyorum?
Edward Falk

120

Araç setinin söylediklerini yapın. Böyle bir şey yapmayın:

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

7
Sınıf başka bir sınıfı genişletirse, bu gerçek sınıfı döndürmez, yalnızca temel sınıfı döndürür.
Luis Soeiro

1
@LuisSoeiro Ben bu yöntemin tanımlanmış sınıf döndürür inanıyorum. Temel sınıf statik içeriğe nasıl faktörler emin değilim.
Tom Hawtin - tackline

8
Neden getClass () statik olamaz anlamıyorum. O zaman bu "deyim" gerekmeyecekti.
mmirwaldt

1
@mmirwaldt getClassçalışma zamanı türünü döndürür, bu nedenle statik olamaz.
Tom Hawtin - tackline

5
Gerçek cevap bu. Çünkü sınıfınızın adını kodunuza kendiniz yazmanıza gerek yoktur.
Bobs

99

Java 7 ve sonraki sürümlerinde bunu statik yöntem / alanlarda yapabilirsiniz:

MethodHandles.lookup().lookupClass()

Söyleyecektim Reflection.getCallerClass(). Ancak 'güneş' paketlerinde olmak konusunda bir uyarı verir. Yani bu daha iyi bir çözüm olabilir.
Foumpie

1
@Foumpie: Java 9, bu gayri resmi Reflection.getCallerClass()şeyin yerini alacak resmi bir API tanıtacak . Önemsiz operasyonu için biraz karmaşık Optional<Class<?>> myself = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .walk(s -> s.map(StackWalker.StackFrame::getDeclaringClass) .findFirst());, ama elbette, bu çok daha güçlü olacağı gerçeğiyle bağlantılı.
Holger

6
Bu kolayca en iyi çözümdür. Gerçek sınıf adını belirtme gereğini ortadan kaldırır, belirsiz değildir, bir hack değildir ve Artyom Krivolapov'un gönderisine göre de en hızlı yaklaşımdır.
skomisa

@Rein Bu temel sınıf çağrılmışsa, çalışma zamanı sınıfını almanın bir yolu var mı?
kayma

59

Dolayısıyla, açık bir MyClass.classsözdizimi kullanımı olmadan sınıf nesnesini veya sınıf tam / basit adını statik olarak almamız gerektiğinde bir durumumuz vardır .

Bazı durumlarda gerçekten kullanışlı olabilir, örn. üst düzey işlevler (bu durumda kotlin, kotlin kodundan erişilemeyen statik bir Java sınıfı oluşturur).

Bu bilgiyi almak için birkaç farklı varyantımız var:

  1. new Object(){}.getClass().getEnclosingClass();
    Tom Hawtin tarafından not edildi - tackline

  2. getClassContext()[0].getName();dan SecurityManager
    tarafından belirtildiği Christoffer

  3. new Throwable().getStackTrace()[0].getClassName();
    tarafından count ludwig

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

  5. ve son
    MethodHandles.lookup().lookupClass();
    olarak Rein'den harika


Hazırladım tüm değişkenler ve sonuçlar için temel ölçüt:

# 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


Sonuçlar

  1. Kullanmak için en iyi varyant , oldukça temiz ve canavarca hızlı.
    Yalnızca Java 7 ve Android API 26'dan beri kullanılabilir!
 MethodHandles.lookup().lookupClass();
  1. Android veya Java 6 için bu işlevselliğe ihtiyacınız varsa, ikinci en iyi varyantı kullanabilirsiniz. Oldukça hızlıdır, ancak her kullanım yerinde anonim bir sınıf oluşturur :(
 new Object(){}.getClass().getEnclosingClass();
  1. Birçok yerde ihtiyacınız varsa ve tonlarca anonim ders nedeniyle bayt kodunuzun şişmesini istemiyorsanız SecurityManager, arkadaşınızdır (üçüncü en iyi seçenek).

    Ama sadece arayamazsınız getClassContext()- SecurityManagersınıfta korunur . Bunun gibi bir yardımcı sınıfa ihtiyacınız olacak:

 // 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. Muhtemelen getStackTrace(), istisnayı temel alan veya Thread.currentThread(). Çok verimsizdir ve yalnızca sınıf adını örnek olarak Stringdeğil, a olarak döndürebilir Class<*>.


PS

Statik kotlin araçları (benim gibi :) için bir günlükçü örneği oluşturmak istiyorsanız, bu yardımcıyı kullanabilirsiniz:

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

Kullanım örneği:

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

Bu talimat iyi çalışıyor:

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

5
Gerçekten yavaş olabileceğine dikkat edin. Ancak kopyalayıp yapıştırabilirsiniz.
Gábor Lipták

1
Bunun, her kullanışınızda bir Nesne veya İş Parçacığı oluşturmak zorunda kalmamanın ek bir yararı vardır.
Erel Segal-Halevi

5
@ErelSegalHalevi Arka planda bir sürü StackTraceElement s oluşturur :(
Navin

4
Eğer kaynak koduna bakarsanız, çağrılma durumundan Thread.getStackTrace()başka bir şey yapmadığını göreceksiniz . Yani @count ludwig'in çözümü, bunu başarmanın daha doğrudan yoludur. return (new Exception()).getStackTrace();currentThread()
T-Bull

34

Bunun gibi JNI kullanarak gerçekten tatlı bir şey yapabilirsiniz:

MyObject.java:

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

    public static native String getClassName();

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

sonra:

javac MyObject.java
javah -jni MyObject

sonra:

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

Sonra C denilen paylaşılan bir kütüphane içine derlemek libclassname.sove java çalıştırın!

*kıkırdama


Bu neden yerleşik değil?
ggb667

Zeki ve mizahı takdir ediyorum. Merhemdeki sinek, C işlevinin varsayılan Java_MyObject_getClassNameadının katıştırılmış adı olmasıdır. Bunun yolu JNI'yi kullanmaktır RegisterNatives. Tabii ki bunu JNI ile beslemelisin FindClass(env, 'com/example/MyObject'), bu yüzden orada da galibiyet yok.
Renate

2
@Renate, yani tüm cevap aslında bir şaka mı? Eğer öyleyse, lütfen bunu çok açık yapın çünkü biliyorsunuz, diğer insanlara yardım etmemiz gerekiyor, bu yüzden masumları tuzaklara itmeyelim.
Stéphane Gourichon

Benim şakam değildi ve şaka olduğuna dikkat çektim. Bu senaryo için kullanım durumu genellikle sınıfı günlüklerde tanımlamak içindir. Tüm karmaşıklığı ortadan kaldırarak, genellikle aşağıdakilerden birini yapar: private static final String TAG = "MyClass"veya private static final String TAG = MyClass.class.getSimpleName();İkincisi, bir IDE kullanarak küresel sınıf yeniden adlandırma için daha dostudur.
Renate

20

Sınıflarımın üstündeki Log4j Logger'ı başlatmak (veya açıklama eklemek) için bunu kullanıyorum.

PRO: Fırlatılabilir zaten yüklü ve "IO heavy" SecurityManager'ı kullanmadan kaynakları kaydedebilirsiniz.

CON: Bunun tüm JVM'ler için işe yarayıp yaramayacağı hakkında bazı sorular.

// 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 rahatsız olmaz bu yüzden kendi istisna sınıf oluşturun: jhocr.googlecode.com/svn/trunk/src/main/java/com/googlecode/...
4F2E4A2E

1
Bu çözüm kadar korkunç bir şey önerecekseniz, lütfen en azından profesyonellerinizi SecurityManager'ı kötüye kullanmak gibi korkunç bir çözüm yerine MyClass.class.getName () kullanarak doğal çözümle ilişkilendirin.
Søren Boisen

Daha fazla eksileri: çok ayrıntılı; yavaş (bu aslında küçük bir noktadır çünkü sınıf yüklendiğinde yalnızca bir kez çalışır).
toolforger

13

SecurityManager'ı Kötüye Kullanma

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

Veya ayarlanmadıysa, onu genişleten bir iç sınıf kullanın (aşağıdaki örnek Real'in HowTo'sundan utanarak kopyalanmıştır ):

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

9

Tüm paket adının yanında olmasını istiyorsanız, şunu arayın:

String name = MyClass.class.getCanonicalName();

Yalnızca son öğeyi istiyorsanız şu numarayı arayın:

String name = MyClass.class.getSimpleName();

5

Arayanın sınıfının MyClass.class.getName()kelimesi kelimesine kullanımı aslında işi yapar, ancak bu kodu bu sınıf adına ihtiyacınız olan çok sayıda sınıfa / alt sınıfa yaymanız durumunda hataları kopyalamaya / yapıştırmaya eğilimlidir.

Ve Tom Hawtin'in tarifi aslında kötü değil, sadece doğru şekilde pişirmek gerekiyor :)

Alt sınıflardan çağrılabilecek statik yöntemi olan bir temel sınıfınız varsa ve bu statik yöntemin gerçek arayanın sınıfını bilmesi gerekiyorsa, bu aşağıdaki gibi yapılabilir:

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

Aşağıdaki geçici sınıfların tanımını önleyen yeniden düzenleme-güvenli, kes ve yapıştır-güvenli çözüm.

Sınıf adını yöntem adına eklemeye özen göstererek sınıf adını kurtaran statik bir yöntem yazın:

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

ardından statik yönteminizde hatırlayın:

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

Yeniden düzenleme güvenliği, dizelerin kullanımından kaçınarak verilir, kesme ve yapıştırma güvenliği sağlanır, çünkü arayan yöntemini kesip yapıştırırsanız getMyClassName () öğesini "MyClass2" hedefinde bulamazsınız, bu nedenle yeniden tanımlamak ve güncellemek zorunda kalırsınız.


3

Sorudan beri ClassName.class yerine "this.class" gibi bir şey? (Bu soru sınıf adı yerine sınıfla ilgili olduğu için tartışılabilir) için bu bir kopya olarak işaretlenir, ben cevap burada gönderiyorum:

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

Tanımlanması önemlidir thisClassolarak özel çünkü:
türetilmiş sınıfları ya kendi tanımlamak gerekir: 1) miras olmamalıdır thisClassya da bir hata mesajı üretir
olarak yapılmalıdır 2) diğer sınıflardan referanslar ClassName.classyerine ClassName.thisClass.

İle thisClasstanımlanan, sınıfının adıyla erişim haline gelir:

thisClass.getName()

1

Birden fazla sınıfın statik yöntemlerinde sınıf adını gerekiyordu, bu yüzden aşağıdaki yöntemle bir JavaUtil Sınıfı uyguladım:

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

Umarım yardımcı olur!


1
Sihirli sayı 2 (bu kolayca bir NullPointerException ile sonuçlanabilir) nedeniyle kullanmak kötü değil, aynı zamanda sanal makinenin doğruluğuna da büyük ölçüde güveniyorsunuz. Yöntemin javadoc'undan: * Bazı sanal makineler, bazı durumlarda, yığın izlemeden bir veya daha fazla yığın çerçevesini atlayabilir. Aşırı durumda, bu iş parçacığıyla ilgili yığın izleme bilgisi olmayan bir sanal makinenin bu yöntemden sıfır uzunluklu bir dizi döndürmesine izin verilir. *
Shotgun

0

Her ikisine de bu iki yaklaşım kullandık staticvenon static senaryo:

Ana sınıf:

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

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

Sınıf adı nasıl verilir:

statik olmayan yol:

private AndroidLogger mLogger = new AndroidLogger(this);

Statik yolu:

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

-1

Yansıma kullanıyorsanız, Method nesnesini alabilir ve ardından:

method.getDeclaringClass().getName()

Yöntemin kendisini almak için muhtemelen şunları kullanabilirsiniz:

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

3
Ve "sınıf adı" nın ne olduğunu nasıl bileceksiniz? :)
alfasin

1
Having Class.forName("class name")zaten bir sınıf verir. Neden Method (Yöntem) ile almak istiyorsunuz?
Sergey Irisov
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.