Come chiamare getClass () da un metodo statico in Java?


350

Ho una classe che deve avere alcuni metodi statici. All'interno di questi metodi statici ho bisogno di chiamare il metodo getClass () per effettuare la seguente chiamata:

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

Tuttavia Eclipse mi dice:

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

Qual è il modo appropriato per correggere questo errore di compilazione?


L'uso getResource()prima che esista un'istanza di una classe definita dall'utente (ad es. Non-J2SE) a volte fallisce. Il problema è che JRE utilizzerà il caricatore di classi bootstrap in quella fase, che non avrà risorse applicative sul percorso di classe (del caricatore di bootstrap).
Andrew Thompson,

Risposte:


616

La risposta

Basta usare TheClassName.classinvece di getClass().

Dichiarazione dei logger

Dal momento che ciò attira così tanta attenzione per un caso d'uso specifico - per fornire un modo semplice per inserire dichiarazioni di registro - ho pensato di aggiungere i miei pensieri su questo. I framework di log spesso prevedono che il log sia vincolato a un determinato contesto, ad esempio un nome di classe completo. Quindi non sono copiabili senza modifiche. I suggerimenti per le dichiarazioni di registro a prova di incolla sono forniti in altre risposte, ma presentano degli svantaggi come gonfiare il bytecode o aggiungere introspezione di runtime. Non le consiglio queste. Copia e incolla è un problema dell'editor , quindi una soluzione di editor è la più appropriata.

In IntelliJ, consiglio di aggiungere un modello live:

  • Utilizzare "log" come abbreviazione
  • Usa private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);come testo modello.
  • Fai clic su Modifica variabili e aggiungi CLASS usando l'espressione className()
  • Seleziona le caselle per riformattare e abbreviare i nomi FQ.
  • Cambia il contesto in Java: dichiarazione.

Ora se digiti, log<tab>si espanderà automaticamente in

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

E riformatta e ottimizza automaticamente le importazioni per te.


6
Ciò comporterà una copia / incolla e risultati tristi tristi quando il codice errato viene eseguito in una classe diversa.
William Entriken,

1
@WilliamEntriken: l'uso di classi interne anonime non è un'ottima soluzione, ma gonfia il tuo bytecode con file di classe extra per risparmiare un paio di secondi dello sforzo degli sviluppatori. Non sono sicuro di cosa stiano facendo le persone dove devono copiare / incollare dappertutto, ma dovrebbe essere trattato con una soluzione di editor, non un trucco linguistico. ad es. se si utilizza IntelliJ, utilizzare un modello live che può inserire il nome della classe.
Mark Peters,

1
Mi chiedo davvero perché hai ottenuto 4
voti negativi

"Non sono sicuro di cosa stiano facendo le persone dove devono copiare / incollare ovunque" qualcosa di simile all'utilizzo Login Android, che richiede la fornitura di un tag, in genere il nome della classe. E probabilmente ci sono altri esempi. Ovviamente puoi dichiarare un campo per quello (che è pratica comune), ma che è ancora copia e incolla.
user149408,

1
Hai dimenticato una cosa nella soluzione Live Template: fai clic su "Modifica variabili" e nell'espressione per CLASSdigitare className(). Ciò assicurerà che $ CLASS $ sia effettivamente sostituito con il nome della classe, altrimenti uscirà vuoto.
HerbCSO

122

Per quanto riguarda l'esempio di codice nella domanda, la soluzione standard è fare riferimento esplicitamente alla classe con il suo nome, ed è anche possibile fare a meno di getClassLoader()chiamare:

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

Questo approccio ha ancora il retro che non è molto sicuro contro gli errori di copia / incolla nel caso in cui sia necessario replicare questo codice in un numero di classi simili.

E per quanto riguarda la domanda esatta nel titolo, c'è un trucco pubblicato nel thread adiacente :

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

Utilizza una Objectsottoclasse anonima nidificata per acquisire il contesto di esecuzione. Questo trucco ha il vantaggio di essere copia / incolla sicuro ...

Attenzione quando lo si utilizza in una Classe di base da cui altre classi ereditano:

Vale anche la pena notare che se questo frammento è modellato come metodo statico di una classe base, il currentClassvalore sarà sempre un riferimento a quella classe base piuttosto che a qualsiasi sottoclasse che potrebbe utilizzare quel metodo.


23
Di gran lunga la mia risposta preferita. NameOfClass.class è ovvio, ma ciò richiede che tu conosca il nome della tua classe. Il trucco getEnclosingClass non è solo utile per il copia-incolla, ma anche per i metodi di classe base statica che fanno uso dell'introspezione
Domingo Ignacio,

1
Per inciso, Class.getResource()può comportarsi in modo leggermente diverso da ClassLoader.getResource(); vedi stackoverflow.com/a/676273
augurar

68

In Java7 + puoi farlo in metodi / campi statici:

MethodHandles.lookup().lookupClass()

Bella soluzione se hai bisogno solo della chiamata getSimpleName () per stampare la guida dal metodo principale .
Dmitrii Bulashevich,

6
Stavo cercando una soluzione 'copia incolla' per aggiungere logger ovunque, senza cambiare il nome della classe ogni volta. Me l'hai dato: private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Julien Feniou il

La migliore soluzione in cui è supportato, l'inconveniente è che Android non supporta questo fino a API 26, ovvero Android 8.
user149408

24

Ho lottato con questo me stesso. Un bel trucco è usare il thread corrente per ottenere un ClassLoader in un contesto statico. Funzionerà anche in un Hadoop MapReduce. Altri metodi funzionano quando vengono eseguiti localmente, ma restituiscono un InputStream null quando utilizzato in MapReduce.

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


11

getClass() il metodo è definito nella classe Object con la seguente firma:

Classe finale pubblica getClass ()

Poiché non è definito come static, non è possibile chiamarlo in un blocco di codice statico. Vedere queste risposte per ulteriori informazioni: Q1 , Q2 , Q3 .

Se ti trovi in ​​un contesto statico, devi usare l'espressione letterale della classe per ottenere la Classe, quindi in pratica devi fare come:

Foo.class

Questo tipo di espressione si chiama Class Literals e sono spiegati nel libro delle specifiche del linguaggio Java come segue:

Un letterale di classe è un'espressione consistente nel nome di una classe, interfaccia, matrice o tipo primitivo seguito da un `. ' e la classe token. Il tipo di una classe letterale è Classe. Valuta l'oggetto Class per il tipo denominato (o per vuoto) come definito dal caricatore di classi di definizione della classe dell'istanza corrente.

Puoi anche trovare informazioni su questo argomento nella documentazione dell'API per Class.


9

Provalo

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

O

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

Il bello di questo approccio è che funzionerà anche su versioni Android precedenti alla 8 (API 26). Attenzione che l'indice [1]farà sì che tutti i messaggi di registro vengano segnalati Threadcome origine su Android 4.4.4. [2]sembra dare il risultato corretto su Android e OpenJRE.
user149408

0

Supponiamo che ci sia una classe Utility, quindi il codice di esempio sarebbe -

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

CustomLocation: se una struttura di cartelle all'interno delle risorse rimuove altrimenti questa stringa letterale.


-2

Prova qualcosa del genere. Per me funziona. Logg (nome classe)

    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
Non so perché stai fornendo la stessa risposta che è già in questa discussione tre volte: due volte dal 2011, una volta dal 2013.
Antares42

Questa soluzione funziona se la classe Logg è caricata dal programma di caricamento classi con accesso alla cartella "risorse \ config". Su alcuni server che hanno più classloader (ad esempio un classloader per caricare la cartella lib e altri per caricare le librerie e le risorse delle app), ciò non è vero. Per questo motivo, la soluzione di poupose su questa risposta funziona solo a volte per coincidenza!
Manuel Romeiro,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.