Como chamar getClass () de um método estático em Java?

350

Eu tenho uma classe que deve ter alguns métodos estáticos. Dentro desses métodos estáticos, preciso chamar o método getClass () para fazer a seguinte chamada:

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

No entanto, o Eclipse me diz:

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

Qual é a maneira apropriada de corrigir esse erro de tempo de compilação?

Rama
fonte
O uso getResource()antes de haver uma instância de uma classe definida pelo usuário (por exemplo, não J2SE) às vezes falha. O problema é que o JRE estará usando o carregador de classes de autoinicialização nesse estágio, que não terá recursos de aplicativos no caminho da classe (do carregador de autoinicialização).
Andrew Thompson

Respostas:

616

A resposta

Basta usar em TheClassName.classvez de getClass().

Declarando registradores

Como isso chama muita atenção para uma base de dados específica - para fornecer uma maneira fácil de inserir declarações de log -, pensei em acrescentar meus pensamentos sobre isso. As estruturas de log geralmente esperam que o log seja restrito a um determinado contexto, digamos um nome de classe totalmente qualificado. Portanto, eles não são passíveis de cópia sem modificação. Sugestões para declarações de log seguras para colar são fornecidas em outras respostas, mas têm desvantagens, como inflar o bytecode ou adicionar a introspecção em tempo de execução. Eu não recomendo estes. Copiar e colar é uma preocupação do editor , portanto, uma solução de editor é mais apropriada.

No IntelliJ, recomendo adicionar um modelo ativo:

  • Use "log" como abreviação
  • Use private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);como o texto do modelo.
  • Clique em Editar variáveis ​​e adicione CLASS usando a expressão className()
  • Marque as caixas para reformatar e reduzir nomes de FQ.
  • Mude o contexto para Java: declaração.

Agora, se você digitar, log<tab>ele será expandido automaticamente para

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

Reformate e otimize automaticamente as importações para você.

Mark Peters
fonte
6
Isso resultará em copiar / colar e tristes resultados tristes quando o código incorreto estiver sendo executado em uma classe diferente.
William Entriken
11
@WilliamEntriken: Usar classes internas anônimas não é uma ótima solução para isso, apenas enche seu bytecode com arquivos de classe extras para economizar alguns segundos de esforço do desenvolvedor. Não sei ao certo o que as pessoas estão fazendo onde precisam copiar / colar em todo o lugar, mas isso deve ser tratado com uma solução de editor, não com um hack de idioma. por exemplo, se estiver usando o IntelliJ, use um modelo ao vivo que pode inserir o nome da classe.
Mark Peters
11
Eu realmente me pergunto porque você tem 4 downvotes
Al-Mothafar
“Não sei ao certo o que as pessoas estão fazendo onde precisam copiar / colar em todo o lugar” - algo como o uso Logno Android, que exige que uma tag seja fornecida, geralmente o nome da classe. E provavelmente existem outros exemplos. Claro que você pode declarar um campo para isso (que é uma prática comum), mas que ainda é copiar e colar.
User149408 28/04
11
Você esqueceu uma coisa na sua solução Live Template: Clique em "Editar variáveis" e, em Expressão, para CLASSdigitar className(). Isso garantirá que $ CLASS $ seja realmente substituído pelo nome da classe, caso contrário, ele ficará vazio.
HerbCSO
122

Quanto ao exemplo de código na pergunta, a solução padrão é referenciar a classe explicitamente pelo nome, e é até possível sem a getClassLoader()chamada:

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

Essa abordagem ainda tem um verso: não é muito seguro contra erros de copiar / colar, caso você precise replicar esse código para várias classes semelhantes.

E quanto à pergunta exata no título, há um truque publicado no segmento adjacente :

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

Ele usa uma Objectsubclasse anônima aninhada para se apossar do contexto de execução. Esse truque tem a vantagem de ser seguro copiar / colar ...

Cuidado ao usar isso em uma Classe Base que outras classes herdam de:

Também é importante notar que, se esse snippet for modelado como um método estático de alguma classe base, o currentClassvalor sempre será uma referência a essa classe base e não a qualquer subclasse que possa estar usando esse método.

Sergey Ushakov
fonte
23
De longe a minha resposta favorita. NameOfClass.class é óbvio, mas isso exige que você saiba o nome da sua classe. O truque getEnclosingClass não é apenas útil para copiar e colar, também útil para métodos de classe base estáticos que fazem uso de introspecção
Domingo Ignacio
11
Aliás, Class.getResource()pode se comportar um pouco diferente de ClassLoader.getResource(); Veja stackoverflow.com/a/676273
augurar
68

No Java7 +, você pode fazer isso em métodos / campos estáticos:

MethodHandles.lookup().lookupClass()
Rédea
fonte
Solução agradável se você precisar apenas da chamada getSimpleName () para imprimir a ajuda do método principal .
Dmitrii Bulashevich
6
Eu estava procurando uma solução 'copiar e colar' para adicionar logger em todos os lugares, sem alterar o nome da classe a cada vez. Você me deu: private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Julien Feniou 22/10
A melhor solução em que é suportado, a desvantagem é que o Android não suporta isso até a API 26, ou seja, Android 8.
user149408
24

Eu lutei com isso sozinho. Um bom truque é usar o thread atual para obter um ClassLoader quando estiver em um contexto estático. Isso funcionará também no Hadoop MapReduce. Outros métodos funcionam ao executar localmente, mas retornam um InputStream nulo quando usado em um MapReduce.

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

Basta usar uma classe literal, ou seja, NameOfClass.class

Michael Borgwardt
fonte
11

getClass() O método é definido na classe Object com a seguinte assinatura:

classe pública final getClass ()

Como não está definido como static, você não pode chamá-lo em um bloco de código estático. Veja estas respostas para obter mais informações: Q1 , Q2 , Q3 .

Se você estiver em um contexto estático, precisará usar a expressão literal da classe para obter a classe; portanto, basicamente você deve fazer o seguinte:

Foo.class

Esse tipo de expressão é chamado de Literais de Classe e são explicados no Livro de Especificação de Linguagem Java da seguinte maneira:

Um literal de classe é uma expressão que consiste no nome de uma classe, interface, matriz ou tipo primitivo seguido de um `. ' e a classe de token. O tipo de um literal de classe é Class. Ele avalia no objeto Class o tipo nomeado (ou void), conforme definido pelo carregador de classes definidor da classe da instância atual.

Você também pode encontrar informações sobre este assunto na documentação da API para Class.

melihcelik
fonte
9

Tente

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

Ou

Thread.currentThread().getStackTrace()[2].getClassName()
Ishfaq
fonte
A vantagem dessa abordagem é que ela também funcionará nas versões do Android anteriores à 8 (API 26). Cuidado com o índice que [1]fará com que todas as mensagens de log sejam relatadas Threadcomo origem no Android 4.4.4. [2]parece dar o resultado correto no Android e no OpenJRE.
user149408 20/05
0

Suponha que exista uma classe Utility, o código de exemplo seria -

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

CustomLocation - se houver alguma estrutura de pastas nos recursos, remova essa literal de cadeia de caracteres.

Pravind Kumar
fonte
-2

Tente algo assim. Funciona para mim. Logg (nome da 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
fonte
11
Não sei por que você está fornecendo a mesma resposta que já está em esta discussão três vezes: duas vezes a partir de 2011, uma vez que a partir de 2013.
Antares42
Esta solução funciona se a classe Logg for carregada pelo carregador de classes com acesso à pasta "resources \\ config". Em alguns servidores que possuem vários carregadores de classe (por exemplo, um carregador de classe para carregar a pasta lib e outro para carregar libs e recursos de aplicativos), isso não é verdade. Por esse motivo, a solução utilizada nesta resposta só funciona às vezes por coincidência!
Manuel Romeiro 14/08