Существует ли общий способ получить имя класса Java в статическом контексте? [Дубликат]

Это происходит из-за того, что неявный фид строки, также известный как символ новой строки \n, добавляется ко всем пользовательским вводам терминала, поскольку он сообщает потоку начать новую строку. Вы можете смело учитывать это, используя std::getline при проверке нескольких строк ввода пользователя. Поведение по умолчанию std::getline будет читать все до и включая символ новой строки \n из объекта входного потока, который в этом случае является std::cin.

#include 
#include 

int main()
{
    std::string name;
    std::string state;

    if (std::getline(std::cin, name) && std::getline(std::cin, state))
    {
        std::cout << "Your name is " << name << " and you live in " << state;
    }
    return 0;
}
Input:

"John"
"New Hampshire"

Output:

"Your name is John and you live in New Hampshire"
blockquote >

207
задан Miles D 1 June 2009 в 21:42
поделиться

15 ответов

Поскольку вопрос Что-то вроде `this.class` вместо` ClassName.class`? помечен как дубликат для этого (что можно утверждать, потому что этот вопрос касается класса, а не класса name), я отправляю ответ здесь:

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()
197
ответ дан Community 21 August 2018 в 12:24
поделиться
  • 1
    Если вы собираетесь использовать жесткий код в знании MyClass, то вы также можете просто написать String name = & quot; MyClass & quot ;; ! – John Topley 1 June 2009 в 21:45
  • 2
    Но тогда рефакторинг имени класса в вашей среде IDE не будет работать должным образом. – James Van Huis 1 June 2009 в 21:48
  • 3
    Правда. Хотя MyClass.class гарантирует, что эта строка не забудется с рефакторингом «change class name» – toolkit 1 June 2009 в 21:50
  • 4
    I пожелает "this & quot; работал в статическом контексте, чтобы обозначить текущий класс на Java, что «class.xxx» было разрешено в любом случае или в статическом коде для обозначения этого класса! Проблема в том, что MyClass является многословным и избыточным в контексте. Но тогда, насколько мне нравится Java, похоже, он склоняется к многословию. – Lawrence Dol 2 June 2009 в 05:41
  • 5
    Что делать, если я вызываю статический метод в подклассе, и я хочу имя подкласса? – Edward Falk 26 October 2012 в 16:49
202
ответ дан Community 1 November 2018 в 06:16
поделиться
202
ответ дан Community 1 November 2018 в 06:16
поделиться

Я использовал эти два подхода для сценариев static и non static:

Основной класс:

//For non static approach
public AndroidLogger(Object classObject) {
    mClassName = classObject.getClass().getSimpleName();
}

//For static approach
public AndroidLogger(String className) {
    mClassName = className;
}

Как указать имя класса:

нестационарный способ:

private AndroidLogger mLogger = new AndroidLogger(this);

Статический путь:

private static AndroidLogger mLogger = new AndroidLogger(Myclass.class.getSimpleName());
0
ответ дан 0xalihn 21 August 2018 в 12:24
поделиться

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

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

У нас есть несколько разных вариантов для получения этого info:

  1. new Object(){}.getClass().getEnclosingClass();, отмеченный Том Хотин - галтика
  2. getClassContext()[0].getName(); из SecurityManager, отмеченный Кристоффер
  3. new Throwable().getStackTrace()[0].getClassName(); count ludwig
  4. Thread.currentThread().getStackTrace()[1].getClassName(); из Keksi
  5. и наконец, удивительный MethodHandles.lookup().lookupClass(); из 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

Выводы

  1. Лучший вариант использования, довольно чистый и чудовищно быстрый. Доступно только с 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<*>.

PS

Если вы хотите создать экземпляр регистратора для статического 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
}
25
ответ дан Artyom Krivolapov 21 August 2018 в 12:24
поделиться

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

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

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

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

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

Рефакторинг безопасности предоставляется, избегая использования строк, разрезание и защита пасты предоставляется, потому что если вы вырезаете и вставляете метод вызова, вы не найдете getMyClassName () в целевом классе «MyClass2», поэтому вам придется переопределять и обновлять его.

4
ответ дан avalori 21 August 2018 в 12:24
поделиться

Я использую это, чтобы запустить 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()); 
37
ответ дан bluish 21 August 2018 в 12:24
поделиться
  • 1
    Позаботьтесь, чтобы это было очень медленно. Однако вы можете скопировать его. – Gábor Lipták 12 November 2012 в 15:12
  • 2
    Это имеет дополнительное преимущество в том, что вам не нужно создавать объект или поток каждый раз, когда вы его используете. – Erel Segal-Halevi 1 January 2013 в 14:46
  • 3
    @ErelSegalHalevi Он создает много StackTraceElement s в фоновом режиме, хотя :( – Navin 3 November 2013 в 04:43
  • 4
    Если вы посмотрите на исходный код Thread.getStackTrace(), вы увидите, что он не делает ничего, кроме return (new Exception()).getStackTrace(); в случае вызова на currentThread(). Таким образом, решение @count ludwig - это более прямой способ добиться того же. – T-Bull 16 November 2013 в 15:44
  • 5
    Создайте свой собственный класс исключений, чтобы jvm не беспокоился: jhocr.googlecode.com/svn/trunk/src/main/java/com/googlecode/… – 4F2E4A2E 17 December 2013 в 23:46
  • 6
    Если вы собираетесь предлагать что-то столь же ужасное, как это решение, пожалуйста, по крайней мере, свяжите свои профи с естественным решением использования MyClass.class.getName () вместо другого ужасного решения, например, злоупотребления SecurityManager. – Søren Boisen 30 May 2015 в 19:58
  • 7
    Еще минусы: слишком многословный; slow (на самом деле это небольшая точка, потому что она запускается только один раз, когда класс загружается). – toolforger 28 November 2017 в 13:37

Нарушить SecurityManager

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

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

public static class CurrentClassGetter extends SecurityManager {
    public String getClassName() {
        return getClassContext()[1].getName(); 
    }
}
13
ответ дан Christoffer 21 August 2018 в 12:24
поделиться

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

method.getDeclaringClass().getName()

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

Class<?> c = Class.forName("class name");
Method  method = c.getDeclaredMethod ("method name", parameterTypes)
-1
ответ дан Franzé Jr. 21 August 2018 в 12:24
поделиться
  • 1
    И как вы узнаете, что такое: & quot; имя класса & quot; ? :) – alfasin 7 July 2016 в 19:42
  • 2
    Наличие Class.forName("class name") уже дает вам класс. Почему вы хотите получить его с помощью метода? – Sergey Irisov 14 September 2016 в 07:34

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

String name = MyClass.class.getCanonicalName();

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

String name = MyClass.class.getSimpleName();
9
ответ дан James Van Huis 21 August 2018 в 12:24
поделиться

Вы можете сделать что-то действительно сладкое, используя 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() );
    }
}

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

Затем скомпилируйте C вверх в общую библиотеку с именем libclassname.so и запустите java!

* chuckle

34
ответ дан lifelongcoug 21 August 2018 в 12:24
поделиться
  • 1
    Почему это не встроено? – ggb667 3 April 2014 в 21:39
  • 2
    Умный, и я ценю юмор. Муха в мазе состоит в том, что имя по умолчанию функции C Java_MyObject_getClassName имеет встроенное имя. Кстати, это использовать JNI RegisterNatives. Конечно, вам придется кормить это с помощью JNI FindClass(env, 'com/example/MyObject'), так что победить там тоже не получится. – Renate 29 November 2014 в 13:36
  • 3
    @Renate, так что весь этот ответ на самом деле шутка? Если да, пожалуйста, сделайте это очень явным, потому что вы знаете, мы должны помогать другим людям, поэтому давайте не будем пускать невинных в ловушки. – Stéphane Gourichon 9 June 2016 в 14:46
  • 4
    Ну, это была не моя шутка, и я заметил, что это шутка. Вариант использования для этого сценария обычно используется для идентификации класса в журналах. Устраняя всю сложность, он обычно сводится к выполнению: private static final String TAG = "MyClass" или private static final String TAG = MyClass.class.getSimpleName();. Второй более дружелюбен к переименованию глобального класса с использованием среды IDE. – Renate 30 June 2016 в 11:30

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

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

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

1
ответ дан phaderer 21 August 2018 в 12:24
поделиться
  • 1
    Не только плохо использовать это из-за магического числа 2 (что может легко привести к исключению NullPointerException), но вы сильно полагаетесь на точность виртуальной машины. Из javadoc метода: * Некоторые виртуальные машины могут при некоторых обстоятельствах опустить один или несколько кадров стека из трассировки стека. В крайнем случае виртуальной машине, которая не имеет информации о трассировке стека, относящейся к этому потоку, разрешено возвращать массив нулевой длины из этого метода. * – Shotgun 10 June 2015 в 13:43

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

MethodHandles.lookup().lookupClass()
64
ответ дан Rein 21 August 2018 в 12:24
поделиться
  • 1
    Я собирался сказать Reflection.getCallerClass(). Но это дает предупреждение о том, чтобы быть в пакетах «солнце». Таким образом, это может быть лучшим решением. – Foumpie 26 December 2015 в 23:55
  • 2
    @Foumpie: Java 9 собирается представить официальный API, который заменит эту неофициальную вещь Reflection.getCallerClass(). Это немного сложно для его тривиальной операции, т. Е. Optional<Class<?>> myself = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .walk(s -> s.map(StackWalker.StackFrame::getDeclaringClass) .findFirst());, но, конечно, это связано с тем, что она будет намного более мощной. – Holger 7 February 2017 в 14:54
  • 3
    Это просто лучшее решение. Это позволяет избежать необходимости указывать фактическое имя класса, это не является неясным, это не взломать, и, согласно сообщению Артема Криволапова ниже, это также самый быстрый подход. – skomisa 9 January 2018 в 06:25
  • 4
    @Rein Есть ли способ получить класс выполнения, если он был вызван в базовом классе? – Glide 21 March 2018 в 01:22

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

return new Object() { }.getClass().getEnclosingClass();
111
ответ дан Tom Hawtin - tackline 21 August 2018 в 12:24
поделиться
  • 1
    это кажется менее плохим, чем решения SecurityManager или Throwable ... – Tetsujin no Oni 1 June 2009 в 22:06
  • 2
    Если класс расширяет другой, это не возвращает фактический класс, а только базовый класс. – Luis Soeiro 12 September 2012 в 23:34
  • 3
    @LuisSoeiro Я считаю, что он возвращает класс, в котором метод определен. Я не уверен, как базовый класс влияет на статический контекст. – Tom Hawtin - tackline 13 September 2012 в 01:18
  • 4
    Я не понимаю, почему getClass () не может быть статическим. Эта "идиома" тогда не понадобится. – mmirwaldt 14 August 2013 в 10:52
  • 5
    Это реальный ответ. Потому что вам не нужно писать имя своего класса в свой код самостоятельно. – breceivemail 3 January 2015 в 08:59
37
ответ дан bluish 1 November 2018 в 06:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: