Miles D
Miles D

Reputation: 8060

Getting the class name from a static method in Java

How can one get the name of the class from a static method in that class. For example

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

To put it in context, I actually want to return the class name as part of a message in an exception.

Upvotes: 297

Views: 226284

Answers (15)

Art Shendrik
Art Shendrik

Reputation: 3481

So, we have a situation when we need to statically get class object or a class full/simple name without an explicit usage of MyClass.class syntax.

It can be really handy in some cases, e.g. logger instance for the upper-level functions (in this case kotlin creates a static Java class not accessible from the kotlin code).

We have a few different variants for getting this info:

  1. new Object(){}.getClass().getEnclosingClass();
    noted by Tom Hawtin - tackline

  2. getClassContext()[0].getName(); from the SecurityManager
    noted by Christoffer

  3. new Throwable().getStackTrace()[0].getClassName();
    by count ludwig

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

  5. and finally awesome

    MethodHandles.lookup().lookupClass();
    from Rein


I've prepared a benchmark for all variants and results are:
# 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

Conclusions

  1. Best variant to use, rather clean and monstrously fast.
    Available only since Java 7 and Android API 26!
 MethodHandles.lookup().lookupClass();
  1. In case you need this functionality for Android or Java 6, you can use the second best variant. It's rather fast too, but creates an anonymous class in each place of usage :(
 new Object(){}.getClass().getEnclosingClass();
  1. If you need it in many places and don't want your bytecode to bloat due to tons of anonymous classes – SecurityManager is your friend (third best option).

But you can't just call getClassContext() – it's protected in the SecurityManager class. You will need some helper class like this:

 // 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. You probably don't ever need to use last two variants based on the getStackTrace() from exception or the Thread.currentThread(). Very inefficient and can return only the class name as a String, not the Class<*> instance.

P.S.

If you want to create a logger instance for static kotlin utils (like me :), you can use this helper:

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

Usage example:

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
 * @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
}

Upvotes: 100

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147164

Do what @toolkit says. Do not do anything like this:

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

(Edit: Or if you are using a Java version that came out well after this answer was originally written, use @Rein's answer.)

Upvotes: 122

0xAliHn
0xAliHn

Reputation: 19290

I have used these two approach for both static and non static scenario:

Main class:

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

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

How to provide class name:

non static way:

private AndroidLogger mLogger = new AndroidLogger(this);

Static way:

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

Upvotes: 0

18446744073709551615
18446744073709551615

Reputation: 16882

Since the question Something like `this.class` instead of `ClassName.class`? is marked as a duplicate for this one (which is arguable because that question is about the class rather than class name), I'm posting the answer here:

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

It is important to define thisClass as private because:
1) it must not be inherited: derived classes must either define their own thisClass or produce an error message
2) references from other classes should be done as ClassName.class rather than ClassName.thisClass.

With thisClass defined, access to the class name becomes:

thisClass.getName()

Upvotes: 4

Franz&#233; Jr.
Franz&#233; Jr.

Reputation: 1194

If you are using reflection, you can get the Method object and then:

method.getDeclaringClass().getName()

To get the Method itself, you can probably use:

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

Upvotes: -2

Rein
Rein

Reputation: 2617

In Java 7+ you can do this in static method/fields:

MethodHandles.lookup().lookupClass()

Upvotes: 142

phaderer
phaderer

Reputation: 11

I needed the class name in the static methods of multiple classes so I implemented a JavaUtil Class with the following method :

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

Hope it will help !

Upvotes: 1

toolkit
toolkit

Reputation: 50297

In order to support refactoring correctly (rename class), then you should use either:

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

or (thanks to @James Van Huis):

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

Upvotes: 261

avalori
avalori

Reputation: 437

A refactoring-safe, cut&paste-safe solution that avoids the definition of ad-hoc classes below.

Write a static method that recover the class name having care to include the class name in the method name:

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

then recall it in your static method:

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

Refactoring safety is given by avoiding the use of strings, cut&paste safety is granted because if you cut&paste the caller method you won't find the getMyClassName() in the target "MyClass2" class, so you will be forced to redefine and update it.

Upvotes: 4

Sergey Ushakov
Sergey Ushakov

Reputation: 2483

Verbatim use of caller's class like MyClass.class.getName() actually does the job, but is prone to copy/paste errors if you propagate this code to numerous classes/subclasses where you need this class name.

And Tom Hawtin's recipe is in fact not bad, one just needs to cook it the right way :)

In case you have a base class with a static method that may be called from subclasses, and this static method needs to know the actual caller's class, this may be achieved like the following:

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

Upvotes: 7

lifelongcoug
lifelongcoug

Reputation: 688

You could do something really sweet by using JNI like this:

MyObject.java:

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

    public static native String getClassName();

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

then:

javac MyObject.java
javah -jni MyObject

then:

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

Then compile the C up into a shared library called libclassname.so and run the java!

*chuckle

Upvotes: 35

Keksi
Keksi

Reputation: 447

This instruction works fine:

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

Upvotes: 43

count ludwig
count ludwig

Reputation: 227

I use this to init the Log4j Logger at the top of my classes (or annotate).

PRO: Throwable is already loaded and you might save resources by not using the "IO heavy" SecurityManager.

CON: Some question as to whether this will work for all 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()); 

Upvotes: 21

Christoffer
Christoffer

Reputation: 12928

Abuse the SecurityManager

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

Or, if not set, use an inner class that extends it (example below shamefully copied from Real's HowTo):

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

Upvotes: 14

James Van Huis
James Van Huis

Reputation: 5571

If you want the entire package name with it, call:

String name = MyClass.class.getCanonicalName();

If you only want the last element, call:

String name = MyClass.class.getSimpleName();

Upvotes: 9

Related Questions