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

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

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() не работа, потому что она статична, а "это" не находится в статическом контексте. Как я могу обойти это? И могу ли я сделать это без использования отражения (поскольку я понимаю, что есть много накладных расходов, и это может повлиять на производительность, так как я регистрирую много данных)

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

спасибо!

8 ответов


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

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

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


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

исключение#getStackTrace()

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

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

нить

использование потока еще проще:

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

изменить способ 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");
}

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

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


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

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

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


не будет реализовывать какой-то Синглтон делать то, что вам нужно?

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

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

Utils.do_something(...)

do

new Utils().do_something(...)