Получение имени класса из статического метода в Java

Как получить имя класса из статического метода в этом классе. Например

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

Чтобы выразить это в контексте, я действительно хочу вернуть имя класса как часть сообщения в исключении.

Ответ 1

Чтобы правильно поддерживать рефакторинг (переименовать класс), вы должны использовать либо:

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

или (спасибо @James Van Huis):

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

Ответ 2

Сделайте то, что говорит инструментарий. Не делайте ничего подобного:

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

Ответ 3

В Java 7+ вы можете сделать это в статическом методе/полях:

MethodHandles.lookup().lookupClass()

Ответ 4

Эта инструкция отлично работает:

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

Ответ 5

Вы можете сделать что-то действительно сладкое, используя JNI следующим образом:

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 и запустите java!

* смешок

Ответ 6

Я использую это, чтобы запустить Log4j Logger в верхней части моих классов (или аннотировать).

PRO: Throwable уже загружен, и вы можете сэкономить ресурсы, не используя "IO heavy" SecurityManager.

CON: Вопрос о том, будет ли это работать для всех JVM.

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

Ответ 7

Нарушить SecurityManager

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

Или, если не задано, используйте внутренний класс, который расширяет его (пример ниже постыдно скопирован из Real HowTo):

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

Ответ 8

Если вы хотите, чтобы с ним было все имя пакета, вызовите:

String name = MyClass.class.getCanonicalName();

Если вы хотите только последний элемент, вызовите:

String name = MyClass.class.getSimpleName();

Ответ 9

Итак, мы имеем ситуацию, когда нам нужно статически получить объект класса или полное/простое имя класса без явного использования синтаксиса MyClass.class.

Это может быть очень удобно в некоторых ситуациях, например. logger для функции верхнего уровня ( в этом случае kotlin создает статический класс Java, недоступный из кода kotlin).

У нас есть несколько разных вариантов получения этой информации:

  • new Object(){}.getClass().getEnclosingClass();
    отметил Том Хотин - галтика

  • getClassContext()[0].getName(); из SecurityManager
    отмеченный Christoffer

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

  • Thread.currentThread().getStackTrace()[1].getClassName();
    из Кекси

  • и, наконец, потрясающий MethodHandles.lookup().lookupClass();
    из Rein


Я подготовил jmh для всех варианты и результаты:

# 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


Выводы

  • Лучший вариант использования, довольно чистый и чудовищно быстрый.
    Доступно только с Java 7 и Android API 26!
 
 MethodHandles.lookup().lookupClass();
  1. Если вам нужна эта функция для Android или Java 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<*>.


P.S.

Если вы хотите создать экземпляр журнала для статических kotlin utils (например, я:), вы можете использовать этот помощник:

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
}

Ответ 10

Вербальное использование класса вызывающего абонента, такого как MyClass.class.getName(), действительно выполняет задание, но подвержено ошибкам копирования/вставки, если вы распространяете этот код на множество классов/подклассов, где вам нужно это имя класса.

И рецепт Tom Hawtin на самом деле неплох, нужно просто приготовить его правильно:)

Если у вас есть базовый класс со статическим методом, который может быть вызван из подклассов, и этот статический метод должен знать фактический класс вызывающего абонента, это может быть достигнуто следующим образом:

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

Ответ 11

Рефакторинг-безопасное, разрезанное и вставное безопасное решение, которое позволяет избежать определения классов ad-hoc ниже.

Напишите статический метод, который восстанавливает имя класса, имея в виду включение имени класса в имя метода:

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

то вспомните его в своем статическом методе:

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

Безопасность рефакторинга дается, избегая использования струн, разрезание и безопасность пасты предоставляются, потому что, если вы разрезаете и вставляете метод вызова, вы не найдете getMyClassName() в целевом классе "MyClass2", так что вы будете вынужден переопределять и обновлять его.

Ответ 12

Поскольку вопрос Что-то вроде` this.class` вместо `ClassName.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 как private, потому что:
1) он не должен наследоваться: производные классы должны либо определить свой собственный thisClass, либо создать сообщение об ошибке
2) ссылки из других классов должны выполняться как ClassName.class, а не ClassName.thisClass.

При определении thisClass доступ к имени класса становится следующим:

thisClass.getName()

Ответ 13

Мне нужно имя класса в статических методах нескольких классов, поэтому я реализовал JavaUtil Class со следующим методом:

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

Надеюсь, это поможет!

Ответ 14

Если вы используете отражение, вы можете получить объект Method, а затем:

method.getDeclaringClass().getName()

Чтобы получить сам метод, вы, вероятно, можете использовать:

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