Comment appeler getClass () à partir d'une méthode statique en Java?

350

J'ai une classe qui doit avoir des méthodes statiques. À l'intérieur de ces méthodes statiques, j'ai besoin d'appeler la méthode getClass () pour effectuer l'appel suivant:

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

Cependant Eclipse me dit:

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

Quelle est la manière appropriée de corriger cette erreur de temps de compilation?

Rama
la source
L'utilisation getResource()avant qu'il y ait une instance d'une classe définie par l'utilisateur (par exemple non J2SE) échouera parfois. Le problème est que le JRE utilisera le chargeur de classe de démarrage à ce stade, qui n'aura pas de ressources d'application sur le chemin de classe (du chargeur de démarrage).
Andrew Thompson

Réponses:

617

La réponse

Utilisez simplement TheClassName.classau lieu de getClass().

Déclaration des enregistreurs

Étant donné que cela attire tellement l'attention sur un cas d'utilisation spécifique - pour fournir un moyen facile d'insérer des déclarations de journal - j'ai pensé ajouter mes réflexions à ce sujet. Les frameworks de journaux s'attendent souvent à ce que le journal soit contraint à un certain contexte, par exemple un nom de classe complet. Ils ne sont donc pas copiables-collables sans modification. Des suggestions de déclarations de journaux à coller sont fournies dans d'autres réponses, mais elles présentent des inconvénients tels que le gonflement du bytecode ou l'ajout d'une introspection d'exécution. Je ne les recommande pas. Le copier-coller est un éditeur préoccupation de l' , donc une solution d'édition est la plus appropriée.

Dans IntelliJ, je recommande d'ajouter un modèle en direct:

  • Utilisez "log" comme abréviation
  • Utilisation private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class); comme modèle de texte.
  • Cliquez sur Modifier les variables et ajoutez CLASSE à l'aide de l'expression className()
  • Cochez les cases pour reformater et raccourcir les noms FQ.
  • Changez le contexte en Java: déclaration.

Maintenant, si vous tapez, log<tab>il se développera automatiquement

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

Et reformatez et optimisez automatiquement les importations pour vous.

Mark Peters
la source
6
Cela entraînera un copier / coller et de tristes résultats tristes lorsque le code incorrect s'exécutera dans une classe différente.
William Entriken
1
@WilliamEntriken: L'utilisation de classes internes anonymes n'est pas une excellente solution à cela, il gonfle simplement votre bytecode avec des fichiers de classe supplémentaires pour économiser quelques secondes d'effort de développement. Je ne suis pas sûr de ce que les gens font là où ils doivent copier / coller partout, mais cela devrait être traité avec une solution d'édition, pas un hack de langue. Par exemple, si vous utilisez IntelliJ, utilisez un modèle dynamique qui peut insérer le nom de classe.
Mark Peters
1
Je me demande vraiment pourquoi vous avez reçu 4 downvotes
Al-Mothafar
«Je ne suis pas sûr de ce que les gens font là où ils doivent copier / coller partout» - quelque chose comme l'utilisation Logdans Android, qui nécessite la fourniture d'une balise, généralement le nom de la classe. Et il y a probablement d'autres exemples. Bien sûr, vous pouvez déclarer un champ pour cela (ce qui est une pratique courante), mais c'est toujours copier-coller.
user149408
1
Vous avez oublié une chose dans votre solution de modèle en direct: cliquez sur "Modifier les variables" et dans l'expression pour CLASStaper className(). Cela garantira que $ CLASS $ est réellement remplacé par le nom de la classe, sinon il sortira simplement vide.
HerbCSO
122

Quant à l'exemple de code dans la question, la solution standard est de référencer explicitement la classe par son nom, et il est même possible de se passer d' getClassLoader()appels:

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

Cette approche a toujours un côté arrière qui n'est pas très sûr contre les erreurs de copier / coller au cas où vous auriez besoin de répliquer ce code dans un certain nombre de classes similaires.

Et quant à la question exacte dans le titre, il y a une astuce publiée dans le fil adjacent :

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

Il utilise une Objectsous-classe anonyme imbriquée pour saisir le contexte d'exécution. Cette astuce a l'avantage d'être sûre copier / coller ...

Attention lors de l'utilisation dans une classe de base dont les autres classes héritent:

Il convient également de noter que si cet extrait est façonné comme une méthode statique d'une classe de base, la currentClassvaleur sera toujours une référence à cette classe de base plutôt qu'à toute sous-classe qui peut utiliser cette méthode.

Sergey Ushakov
la source
23
De loin ma réponse préférée. NameOfClass.class est évident, mais cela nécessite que vous connaissiez le nom de votre classe. L'astuce getEnclosingClass n'est pas seulement utile pour le copier-coller, elle est également utile pour les méthodes de classe de base statiques qui utilisent l'introspection
Domingo Ignacio
1
Incidemment Class.getResource()peut se comporter légèrement différemment de ClassLoader.getResource(); voir stackoverflow.com/a/676273
augurar
68

Dans Java7 +, vous pouvez le faire dans des méthodes / champs statiques:

MethodHandles.lookup().lookupClass()
Rêne
la source
Belle solution si vous n'avez besoin que de l' appel getSimpleName () pour imprimer l'aide de la méthode principale .
Dmitrii Bulashevich
6
Je cherchais une solution de «copier-coller» pour ajouter un enregistreur partout, sans changer le nom de la classe à chaque fois. Vous me l'avez donné: private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Julien Feniou
La meilleure solution là où elle est prise en charge, l'inconvénient étant qu'Android ne prend pas en charge cela jusqu'à l'API 26, c'est-à-dire Android 8.
user149408
24

J'ai lutté avec ça moi-même. Une bonne astuce consiste à utiliser le thread actuel pour obtenir un ClassLoader dans un contexte statique. Cela fonctionnera également dans un MapReduce Hadoop. D'autres méthodes fonctionnent lors de l'exécution locale, mais retournent un InputStream nul lorsqu'il est utilisé dans un MapReduce.

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

Utilisez simplement un littéral de classe, c.-à-d. NameOfClass.class

Michael Borgwardt
la source
11

getClass() est définie dans la classe Object avec la signature suivante:

classe finale publique getClass ()

Comme il n'est pas défini comme static, vous ne pouvez pas l'appeler dans un bloc de code statique. Voir ces réponses pour plus d'informations: Q1 , Q2 , Q3 .

Si vous êtes dans un contexte statique, vous devez utiliser l'expression littérale de classe pour obtenir la classe, vous devez donc essentiellement faire comme:

Foo.class

Ce type d'expression est appelé Class Literals et ils sont expliqués dans le Java Language Specification Book comme suit:

Un littéral de classe est une expression composée du nom d'une classe, d'une interface, d'un tableau ou d'un type primitif suivi d'un `. ' et la classe des jetons. Le type d'un littéral de classe est Class. Il évalue l'objet Class pour le type nommé (ou pour void) tel que défini par le chargeur de classe définissant la classe de l'instance actuelle.

Vous pouvez également trouver des informations sur ce sujet dans la documentation de l'API pour Class.

melihcelik
la source
9

Essayez-le

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

Ou

Thread.currentThread().getStackTrace()[2].getClassName()
Ishfaq
la source
La beauté de cette approche est qu'elle fonctionnera également sur les versions d'Android antérieures à 8 (API 26). Méfiez-vous de cet index [1], tous les messages de journal seront signalés Threadcomme source sur Android 4.4.4. [2]semble donner le résultat correct sur Android et OpenJRE.
user149408
0

Supposons qu'il existe une classe Utility, alors l'exemple de code serait -

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

CustomLocation - si une structure de dossiers dans les ressources, sinon supprimez ce littéral de chaîne.

Pravind Kumar
la source
-2

Essayez quelque chose comme ça. Ça marche pour moi. Logg (nom de 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");
Oliver Lagerbäck Westblom
la source
1
Je ne sais pas pourquoi vous fournissez la même réponse qui est déjà dans ce sujet trois fois: deux fois en 2011, une fois en 2013.
Antares42
Cette solution fonctionne si la classe Logg est chargée par le chargeur de classe avec accès au dossier "resources \\ config". Sur certains serveurs qui ont plusieurs chargeurs de classe (par exemple un chargeur de classe pour charger le dossier lib et d'autres pour charger les bibliothèques et les ressources d'application), ce n'est pas vrai. Pour cette raison, la solution poupose sur cette réponse ne fonctionne parfois que par coïncidence!
Manuel Romeiro