Reputation: 8060
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
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 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:
new Object(){}.getClass().getEnclosingClass();
noted by Tom Hawtin - tackline
getClassContext()[0].getName();
from the SecurityManager
noted by Christoffer
new Throwable().getStackTrace()[0].getClassName();
by count ludwig
Thread.currentThread().getStackTrace()[1].getClassName();
from Keksi
and finally awesome
MethodHandles.lookup().lookupClass();
from Rein
# 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
MethodHandles.lookup().lookupClass();
new Object(){}.getClass().getEnclosingClass();
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())
}
getStackTrace()
from exception or the Thread.currentThread()
. Very inefficient and can return only the class name as a String
, not the Class<*>
instance.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
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
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
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
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
Reputation: 2617
In Java 7+ you can do this in static method/fields:
MethodHandles.lookup().lookupClass()
Upvotes: 142
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
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
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
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
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
Reputation: 447
This instruction works fine:
Thread.currentThread().getStackTrace()[1].getClassName();
Upvotes: 43
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
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
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