Як викликати getClass () зі статичного методу на Java?


350

У мене є клас, який повинен мати деякі статичні методи. Всередині цих статичних методів мені потрібно викликати метод getClass (), щоб здійснити наступний виклик:

public static void startMusic() {
  URL songPath = getClass().getClassLoader().getResource("background.midi");
}

Однак Eclipse говорить мені:

Cannot make a static reference to the non-static method getClass() 
from the type Object

Який відповідний спосіб виправити цю помилку часу компіляції?


Використання getResource()до того, як з’явиться екземпляр визначеного користувачем (наприклад, не-J2SE) класу, іноді не вдасться. Проблема полягає в тому, що на цьому етапі JRE буде використовувати завантажувач класу завантажувача, який не матиме ресурсів програми на шляху до класу (завантажувача завантажувача).
Ендрю Томпсон,

Відповіді:


616

Відповідь

Просто використовуйте TheClassName.classзамість getClass().

Декларування лісорубів

Оскільки на це приділяється стільки уваги конкретній справі - щоб забезпечити простий спосіб вставлення декларацій журналу - я подумав, що додам свої думки з цього приводу. Рамки журналів часто очікують, що журнал буде обмежений певним контекстом, скажімо, повноцінне ім'я класу. Таким чином, вони не підлягають копіюванню без змін. Пропозиції щодо безпечного вставки в журнал містяться в інших відповідях, але вони мають недоліки, такі як надуття байтового коду або додавання самоаналізу часу виконання. Я не рекомендую їх. Копіювати-вставляти - це проблема редактора , тому рішення редактора є найбільш підходящим.

У IntelliJ я рекомендую додати живий шаблон:

  • Використовуйте "журнал" як абревіатуру
  • Використовувати private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);як текст шаблону.
  • Клацніть Змінити змінні та додайте КЛАС, використовуючи вираз className()
  • Поставте прапорці для переформатування та скорочення імен FQ.
  • Змініть контекст на Java: заяву.

Тепер, якщо ви введете, log<tab>він автоматично розшириться на

private static final Logger logger = LoggerFactory.getLogger(ClassName.class);

І автоматично переформатувати та оптимізувати імпорт для вас.


6
Це призведе до копіювання / вставлення та сумних сумних результатів, коли неправильний код працює в іншому класі.
Вільям Ентрікен

1
@WilliamEntriken: Використання анонімних внутрішніх класів не є прекрасним рішенням для цього, воно просто роздуває ваш байт-код додатковими класними файлами, щоб заощадити пару секунд зусиль розробника. Я не впевнений, що люди роблять там, де їм доведеться копіювати / вставляти всюди, але це повинно бути оброблене рішенням редактора, а не мовним злому. наприклад, якщо ви користуєтеся IntelliJ, використовуйте Живий шаблон, який може вставити ім'я класу.
Марк Петерс

1
Мені дуже цікаво, чому ви отримали 4 голоси
Аль-Мотафар

"Я не впевнений, що люди роблять там, де їм доведеться копіювати / вставляти всюди" - щось таке, як використання Logв Android, для якого потрібен тег, як правило, назва класу. І, мабуть, є й інші приклади. Звичайно, ви можете оголосити поле для цього (що є звичайною практикою), але це все-таки копіювати та вставляти.
користувач149408

1
Ви забули одну річ у своєму рішенні шаблонів Live: натисніть "Редагувати змінні" та в пункті Expression for CLASStype in className(). Це дозволить переконатися, що $ CLASS $ фактично замінено назвою класу, інакше він просто вийде порожнім.
HerbCSO

122

Що стосується прикладу коду у питанні, стандартним рішенням є чітке посилання на клас за його назвою, і це навіть можна зробити без getClassLoader()виклику:

class MyClass {
  public static void startMusic() {
    URL songPath = MyClass.class.getResource("background.midi");
  }
}

Такий підхід все ще має зворотну сторону, що він не дуже безпечний проти помилок копіювання / вставки, якщо вам потрібно скопіювати цей код на ряд подібних класів.

А що стосується точного питання в заголовку, у суміжній нитці є хитрість :

Class currentClass = new Object() { }.getClass().getEnclosingClass();

Для використання Objectконтексту виконання він використовує вкладений анонімний підклас. Ця хитрість має перевагу: безпечне копіювання / вставка ...

Обережно, використовуючи це в базовому класі, що інші класи успадковують:

Варто також зазначити, що якщо цей фрагмент має форму статичного методу деякого базового класу, то currentClassзначення завжди буде посиланням на цей базовий клас, а не на будь-який підклас, який може використовувати цей метод.


23
На сьогоднішній день моя улюблена відповідь. NameOfClass.class очевидний, але це вимагає, щоб ви знали ім'я свого класу. Трюк getEnclosingClass корисний не лише для копіювання, а й для статичних методів базового класу, які використовують самоаналіз
Домінго Ігнасіо

1
Між іншим, Class.getResource()може поводитись трохи інакше, ніж ClassLoader.getResource(); см stackoverflow.com/a/676273
augurar

68

У Java7 + ви можете це зробити в статичних методах / полях:

MethodHandles.lookup().lookupClass()

Приємне рішення, якщо вам потрібен лише виклик getSimpleName () для друку довідки з основного методу.
Дмитро Булашевич

6
Я шукав рішення "копіювати пасту" для додавання реєстратора скрізь, не змінюючи назви класу щоразу. Ви мені це передали: private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Жульєн Феніу

Найкраще рішення, де воно підтримується, недоліком є ​​те, що Android не підтримує це лише до API 26, тобто Android 8.
user149408

24

Я сам боровся з цим. Приємним трюком є ​​використання поточної нитки, щоб отримати ClassLoader, коли в статичному контексті. Це також буде працювати в Hadoop MapReduce. Інші методи працюють при локальному запуску, але повертають нульовий InputStream при використанні в MapReduce.

public static InputStream getResource(String resource) throws Exception {
   ClassLoader cl = Thread.currentThread().getContextClassLoader();
   InputStream is = cl.getResourceAsStream(resource);
   return is;
}


11

getClass() метод визначений у класі Object із наступним підписом:

загальнодоступний фінал Class getClass ()

Оскільки він не визначений як static, ви не можете викликати його в блоці статичного коду. Дивіться ці відповіді для отримання додаткової інформації: Q1 , Q2 , Q3 .

Якщо ви перебуваєте в статичному контексті, то для отримання класу ви повинні використовувати виразне вираження класу, тож вам потрібно робити так:

Foo.class

Цей тип вираження називається Class Literals, і вони пояснюються в Книзі специфікацій мови Java наступним чином:

Буквене позначення класу - це вираз, що складається з імені класу, інтерфейсу, масиву або примітивного типу, що супроводжується символом `. ' і маркер класу. Тип буквального класу - клас. Він оцінює об'єкт Class для названого типу (або для void), як визначено визначальним завантажувачем класу класу поточного екземпляра.

Ви також можете знайти інформацію про цю тему в документації API для Class.


9

Спробуй це

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

Або

Thread.currentThread().getStackTrace()[2].getClassName()

Краса такого підходу полягає в тому, що він також працюватиме на версіях Android до 8 (API 26). Будьте обачні, що цей індекс [1]призведе до того, що всі повідомлення журналу відображатимуться Threadяк джерело на Android 4.4.4. [2]схоже, дає правильний результат і для Android, і для OpenJRE.
користувач149408

0

Припустимо, існує клас утиліти, тоді зразок коду буде -

    URL url = Utility.class.getClassLoader().getResource("customLocation/".concat("abc.txt"));

CustomLocation - якщо будь-яка структура папки в межах ресурсів інакше видаліть цей рядок буквально.


-2

Спробуйте щось подібне. Це працює для мене. Журнал (назва класу)

    String level= "";

    Properties prop = new Properties();

    InputStream in =
            Logg.class.getResourceAsStream("resources\\config");

    if (in != null) {
        prop.load(in);
    } else {
        throw new FileNotFoundException("property file '" + in + "' not found in the classpath");
    }

    level = prop.getProperty("Level");

1
Не впевнений, чому ви
даєте

Це рішення працює, якщо клас Logg завантажується завантажувачем класів з доступом до папки "ресурси \\ config". На деяких серверах, у яких є кілька завантажувачів класів (наприклад, один завантажувач для завантаження папок lib та інший для завантаження файлів і ресурсів додатків), це неправда. З цієї причини рішення, що покладається на цю відповідь, працює лише іноді за збігом обставин!
Мануель Ромейро
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.