¿Cómo llamar a getClass () desde un método estático en Java?

350

Tengo una clase que debe tener algunos métodos estáticos. Dentro de estos métodos estáticos, necesito llamar al método getClass () para hacer la siguiente llamada:

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

Sin embargo, Eclipse me dice:

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

¿Cuál es la forma adecuada de corregir este error de tiempo de compilación?

Rama
fuente
El uso getResource()antes de que haya una instancia de una clase definida por el usuario (por ejemplo, no J2SE) a veces fallará. El problema es que el JRE usará el cargador de clases bootstrap en esa etapa, que no tendrá recursos de aplicación en la ruta de clase (del cargador bootstrap).
Andrew Thompson

Respuestas:

617

La respuesta

Solo use en TheClassName.classlugar de getClass().

Declarando madereros

Dado que esto recibe tanta atención para un caso de uso específico, para proporcionar una manera fácil de insertar declaraciones de registro, pensé en agregar mis pensamientos al respecto. Los marcos de registro a menudo esperan que el registro esté restringido a un determinado contexto, digamos un nombre de clase completamente calificado. Por lo tanto, no son copiables sin modificaciones. En otras respuestas se proporcionan sugerencias para declaraciones de registro seguras para pegar, pero tienen inconvenientes como inflar el código de bytes o agregar introspección en tiempo de ejecución. No los recomiendo. Copiar y pegar es una preocupación del editor , por lo que una solución de editor es la más adecuada.

En IntelliJ, recomiendo agregar una plantilla en vivo:

  • Use "log" como la abreviatura
  • Úselo private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);como texto de plantilla.
  • Haga clic en Editar variables y agregue CLASE usando la expresión className()
  • Marque las casillas para formatear y acortar los nombres de FQ.
  • Cambiar el contexto a Java: declaración.

Ahora, si escribe log<tab>, se expandirá automáticamente a

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

Y reformatee y optimice automáticamente las importaciones por usted.

Mark Peters
fuente
66
Esto dará como resultado copiar / pegar y resultados tristes cuando el código incorrecto se ejecute en una clase diferente.
William Entriken
1
@WilliamEntriken: el uso de clases internas anónimas no es una gran solución para eso, solo aumenta tu bytecode con archivos de clase adicionales para ahorrar un par de segundos de esfuerzo del desarrollador. No estoy seguro de qué están haciendo las personas donde tienen que copiar / pegar en todo el lugar, pero debería tratarse con una solución de editor, no un truco de idioma. por ejemplo, si usa IntelliJ, use una plantilla en vivo que pueda insertar el nombre de clase.
Mark Peters el
1
Realmente me pregunto por qué tienes 4
votos a favor
"No estoy seguro de qué están haciendo las personas donde tienen que copiar / pegar en todo el lugar", algo como usar Logen Android, que requiere que se suministre una etiqueta, generalmente el nombre de la clase. Y probablemente hay otros ejemplos. Por supuesto, puede declarar un campo para eso (que es una práctica común), pero aún así es copiar y pegar.
user149408
1
Olvidó una cosa en su solución Live Template: haga clic en "Editar variables" y en la expresión para CLASSescribir className(). Eso asegurará que $ CLASS $ se reemplace realmente con el nombre de la clase, de lo contrario, simplemente saldrá vacío.
HerbCSO
122

En cuanto al ejemplo de código en la pregunta, la solución estándar es hacer referencia a la clase explícitamente por su nombre, e incluso es posible hacerlo sin getClassLoader()llamar:

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

Este enfoque todavía tiene una parte posterior de que no es muy seguro contra los errores de copiar / pegar en caso de que necesite replicar este código en varias clases similares.

Y en cuanto a la pregunta exacta en el título, hay un truco publicado en el hilo adyacente :

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

Utiliza una Objectsubclase anónima anidada para obtener el contexto de ejecución. Este truco tiene la ventaja de ser seguro para copiar / pegar ...

Tenga cuidado al usar esto en una clase base que otras clases heredan de:

También vale la pena señalar que si este fragmento se forma como un método estático de alguna clase base, el currentClassvalor siempre será una referencia a esa clase base en lugar de a cualquier subclase que pueda estar usando ese método.

Sergey Ushakov
fuente
23
Con mucho, mi respuesta favorita. NameOfClass.class es obvio, pero eso requiere que sepas el nombre de tu clase. El truco getEnclosingClass no solo es útil para copiar y pegar, también es útil para métodos de clase base estáticos que hacen uso de la introspección
Domingo Ignacio
1
Por cierto, Class.getResource()puede comportarse de manera ligeramente diferente de ClassLoader.getResource(); ver stackoverflow.com/a/676273
augurar el
68

En Java7 + puede hacer esto en métodos / campos estáticos:

MethodHandles.lookup().lookupClass()
Rienda
fuente
Buena solución si solo necesita la llamada getSimpleName () para imprimir ayuda desde el método principal .
Dmitrii Bulashevich
66
Estaba buscando una solución de 'copiar y pegar' para agregar el registrador en todas partes, sin cambiar el nombre de la clase cada vez. Me lo diste: private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Julien Feniou
La mejor solución donde es compatible, el inconveniente es que Android no lo admite hasta API 26, es decir, Android 8.
user149408
24

Luché con esto yo mismo. Un buen truco es usar el hilo actual para obtener un ClassLoader cuando se encuentre en un contexto estático. Esto también funcionará en un Hadoop MapReduce. Otros métodos funcionan cuando se ejecutan localmente, pero devuelven un InputStream nulo cuando se usa en un MapReduce.

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

Simplemente use una clase literal, es decir NameOfClass.class

Michael Borgwardt
fuente
11

getClass() El método se define en la clase Object con la siguiente firma:

public final Class getClass ()

Como no está definido como static, no puede llamarlo dentro de un bloque de código estático. Consulte estas respuestas para obtener más información: Q1 , Q2 , Q3 .

Si se encuentra en un contexto estático, debe usar la expresión literal de clase para obtener la Clase, por lo que básicamente debe hacer lo siguiente:

Foo.class

Este tipo de expresión se llama Class Literals y se explican en el Java Language Specification Book de la siguiente manera:

Un literal de clase es una expresión que consiste en el nombre de una clase, interfaz, matriz o tipo primitivo seguido de un '.' y la clase de token. El tipo de un literal de clase es Class. Se evalúa en el objeto Clase para el tipo nombrado (o para vacío) según lo definido por el cargador de clases definitorio de la clase de la instancia actual.

También puede encontrar información sobre este tema en la documentación de la API para la Clase.

melihcelik
fuente
9

Intentalo

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

O

Thread.currentThread().getStackTrace()[2].getClassName()
Ishfaq
fuente
La belleza de este enfoque es que también funcionará en versiones de Android anteriores a la 8 (API 26). Tenga en cuenta que el índice [1]hará que todos los mensajes de registro se informen Threadcomo su fuente en Android 4.4.4. [2]parece dar el resultado correcto tanto en Android como en OpenJRE.
user149408
0

Supongamos que hay una clase de utilidad, entonces el código de muestra sería:

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

CustomLocation: si alguna estructura de carpeta dentro de los recursos, de lo contrario, elimine este literal de cadena.

Pravind Kumar
fuente
-2

Intenta algo como esto. Esto funciona para mi. Logg (nombre de clase)

    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
fuente
1
No estoy seguro de por qué está proporcionando la misma respuesta que ya está en este hilo tres veces: dos veces desde 2011, una desde 2013.
Antares42
Esta solución funciona si el cargador de clases carga la clase Logg con acceso a la carpeta "resources \\ config". En algunos servidores que tienen múltiples cargadores de clases (por ejemplo, un cargador de clases para cargar la carpeta lib y otro para cargar libs y recursos de la aplicación), eso no es cierto. Por esa razón, ¡la solución propuesta en esta respuesta solo funciona a veces por coincidencia!
Manuel Romeiro