Getting the class name from a static method in Java

JavaStatic

Java Problem Overview


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.

Java Solutions


Solution 1 - Java

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

Solution 2 - Java

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

MethodHandles.lookup().lookupClass()

Solution 3 - Java

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, @Rein.)

Solution 4 - Java

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 [tag:kotlin] 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 [tag:JMH] 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();

2. 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();

3. 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())
 }

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

Solution 5 - Java

This instruction works fine:

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

Solution 6 - Java

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

Solution 7 - Java

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

Solution 8 - Java

Abuse the SecurityManager

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

Or, if not set, use an inner class that extends it (example below shamefully copied from http://www.rgagnon.com/javadetails/java-0402.html">Real's HowTo):

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

Solution 9 - Java

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

Solution 10 - Java

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

Solution 11 - Java

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.

Solution 12 - Java

Since the question https://stackoverflow.com/questions/29492993/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()

Solution 13 - Java

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 !

Solution 14 - Java

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

Solution 15 - Java

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)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMiles DView Question on Stackoverflow
Solution 1 - JavatoolkitView Answer on Stackoverflow
Solution 2 - JavaReinView Answer on Stackoverflow
Solution 3 - JavaTom Hawtin - tacklineView Answer on Stackoverflow
Solution 4 - JavaArtyom KrivolapovView Answer on Stackoverflow
Solution 5 - JavaKeksiView Answer on Stackoverflow
Solution 6 - JavalifelongcougView Answer on Stackoverflow
Solution 7 - Javacount ludwigView Answer on Stackoverflow
Solution 8 - JavaChristofferView Answer on Stackoverflow
Solution 9 - JavaJames Van HuisView Answer on Stackoverflow
Solution 10 - JavaSergey UshakovView Answer on Stackoverflow
Solution 11 - JavaavaloriView Answer on Stackoverflow
Solution 12 - Java18446744073709551615View Answer on Stackoverflow
Solution 13 - JavaphadererView Answer on Stackoverflow
Solution 14 - Java0xAliHnView Answer on Stackoverflow
Solution 15 - JavaFranzé Jr.View Answer on Stackoverflow