Java: Как получить объект класса текущего класса из статического контекста?

У меня есть функция регистрации, которая принимает вызывающий объект в качестве параметра. Затем я вызываю getClass(). GetSimpleName(), чтобы я мог легко получить имя класса для добавления в мою запись в журнале для удобства. Проблема в том, что когда я вызываю свою функцию журнала из статического метода, я не могу пройти в "this". Моя функция журнала выглядит примерно так:

public static void log(Object o, String msg){
  do_log(o.getClass().getSimpleName()+" "+msg);
}
public void do_something(){
  log(this, "Some message");
}

Но позвольте сказать, что я хочу войти в статическую функцию:

public static void do_something_static(){
  log(this, "Some message from static");
}

Очевидно, do_something_static() не будет работать, потому что он статичен, а "this" не находится в статическом контексте. Как я могу обойти это? И могу ли я сделать это без использования рефлексии (поскольку я понимаю, что есть много накладных расходов, и это может повлиять на производительность, так как я регистрирую много данных)

Я знаю, что могу, возможно, каким-то образом жестко закодировать текущий класс в вызове, но я уверен, что когда я переведу функцию в другой класс, я забуду обновить жестко закодированную ссылку, и она больше не будет правильный.

Спасибо!

Ответ 1

Вы можете добавить "Класс" в качестве первого параметра и перегрузить метод журнала:

public class SomeClass {
    // Usage test:
    public static void main( String [] args ) {
        log( SomeClass.class, "Hola" );
        log( new java.util.Date(), "Hola" );
    }

    // Object version would call Class method... 
    public static void log( Object o , String msg ) {
        log( o.getClass(), msg );
    }
    public static void log( Class c ,  String message ) {
        System.out.println( c.getSimpleName() + " " + message );
    }
}

Вывод:

$ java SomeClass
SomeClass Hola
Date Hola

Но просто плохо, если вызывающий класс передан в качестве первого аргумента. Здесь, где объектно-ориентированная модель вступает в игру как противоположность "процедурного" стиля.

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

Но, если вы создаете это как переменную класса, тогда будет только один экземпляр класса, если у вас будет 1000 классов, использующих эту утилиту, у вас будет не более 1000 вызовов.

Что-то вроде этого было бы лучше (тонкая вариация этого другого ответа):

public class LogUtility {

    private final String loggingFrom;

    public static LogUtility getLogger() {
        StackTraceElement [] s = new RuntimeException().getStackTrace();
        return new LogUtility( s[1].getClassName() );
    }

    private LogUtility( String loggingClassName ) {
        this.loggingFrom = "("+loggingClassName+") ";
    }

    public void log( String message ) {
        System.out.println( loggingFrom + message );
    }
}

Тест использования:

class UsageClass {
    private static final LogUtility logger = LogUtility.getLogger();

    public static void main( String [] args ) {

        UsageClass usageClass = new UsageClass();

        usageClass.methodOne();
        usageClass.methodTwo();
        usageClass.methodThree();

    }
    private void methodOne() {
        logger.log("One");
    }
    private void methodTwo() {
        logger.log("Two");
    }
    private void methodThree() {
        logger.log("Three");
    }
}

Вывод

$ java UsageClass
(UsageClass) One
(UsageClass) Two
(UsageClass) Three

Обратите внимание на объявление:

....
class UsageClass {
    // This is invoked only once. When the class is first loaded.
    private static final LogUtility logger = LogUtility.getLogger();
....

Таким образом, не имеет значения, если вы используете "регистратор" из объектов A, objectB, objectC или из метода класса (например, main), у всех их будет один экземпляр регистратора.

Ответ 2

запрос к stacktrace может быть суровым для вашей проблемы. У вас есть 3 возможных подхода.

Exception # getStackTrace()

Просто создайте экземпляр исключения и получите первый кадр:

static String className() {
    return new RuntimeException().getStackTrace()[0].getClassName();
}

Тема

Использование Thread еще проще:

static String className2() {
    return Thread.currentThread().getStackTrace()[1].getClassName();
}

эти два подхода имеют тот недостаток, что вы не можете их повторно использовать. Таким образом, вы можете определить для него класс-помощник:

class Helper {

    public static String getClassName() {
        return Thread.currentThread().getStackTrace()[2].getClassName();
    }
} 

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

public static void log(Object o, String msg){
    do_log(Helper.getCClassName() + "  " + msg);
}

Ответ 3

Изменить метод log на:

public static void log(Class c, String msg){
  do_log(c.getSimpleName()+" "+msg);
}

и если do_something_static находится в классе MyClassWithStatics, то do_something_static станет:

public static void do_something_static(){
  log(MyClassWithStatics.class, "Some message from static");
}

Ответ 4

Вместо "this" используйте "MyClass.class" и пусть ваш метод журнала обрабатывает объекты класса без getClass().

Но вместо того, чтобы делать это самостоятельно, подумайте над тем, чтобы позволить фреймворку log сделать это.

Ответ 5

В краткосрочной перспективе вы должны жестко закодировать класс в вызове. Если вы считаете, что это понадобится вам в разных местах вашего проекта, так как это static, вы должны инкапсулировать его и заставить класс воспринимать как параметр.

public static void do_something_static(Class callingClass){
  log(callingClass, "Some message from static");
}

Ответ 6

Ну, если вы действительно не хотите, чтобы hardcode что-то вроде ClassName.class, то вы можете попытаться вывести класс, пройдя стек. К счастью, кто-то уже сделал это:). Кроме того, рассмотрите возможность использования регистратора, который позволяет вам регистрировать что-либо без указания вызывающего объекта.

Ответ 7

Не выполнил бы какой-то Singleton делать то, что вам нужно?

public class LogUtility {

    private final String loggingFrom;
    private static LogUtility instance;

    public static LogUtility getLogger() {
        if(instance == null)
            this.instance = new LogUtility();
        StackTraceElement [] s = new RuntimeException().getStackTrace();
        this.instance.setLoggingFrom(s[1].getClassName())
        return this.instance;
    }

    private LogUtility() {}

    private void setLoggingFrom(String loggingClassName){
        this.loggingFrom = loggingClassName;
    }

    public void log( String message ) {
        System.out.println( loggingFrom + message );
    }
}

Использование (в любом месте вашего проекта):

LogUtility.getLogger().log("Message");

Ответ 8

Замените свои статические функции на нестатические. Вместо

Utils.do_something(...)

делать

new Utils().do_something(...)