É possível tornar estáticas classes anônimas em Java?


123

Em Java, as classes aninhadas podem ser staticou não. Se estiverem static, não contêm uma referência ao ponteiro da instância que os contém (eles também não são mais chamados de classes internas, são chamados de classes aninhadas).

Esquecer de criar uma classe aninhada staticquando não precisar dessa referência pode levar a problemas com a coleta de lixo ou a análise de escape.

É possível criar uma classe interna anônima statictambém? Ou o compilador descobre isso automaticamente (o que poderia acontecer, porque não pode haver nenhuma subclasse)?

Por exemplo, se eu fizer um comparador anônimo, quase nunca preciso da referência externa:

  Collections.sort(list, new Comparator<String>(){
       int compare(String a, String b){
          return a.toUpperCase().compareTo(b.toUpperCase());
       }
  }

Quais são os problemas com a "coleta de lixo ou análise de escape" ao esquecer de tornar estática uma classe interna? Eu pensei que este é sobre o desempenho única ...
Tim Buthe

17
Sua instância de classe interna mantém viva uma referência a sua instância externa, mesmo que você não precise. Isso poderia impedir que as coisas fossem coletadas no lixo. Imagine um objeto de fábrica (com muitos recursos) que cria instâncias leves de algo. Depois que a fábrica tiver feito seu trabalho (por exemplo, durante a inicialização do aplicativo), ele poderá ser descartado, mas isso só funcionará se as coisas que ele criou não se vincularem novamente.
Thilo

Eu sei, isso é apenas um exemplo, mas, como é recorrente, deve-se mencionar que Collections.sort(list, String.CASE_INSENSITIVE_ORDER)funciona desde o Java 2, leia-se, já que a API de coleção existe ...
Holger

Respostas:


138

Não, você não pode, e não, o compilador não pode descobrir isso. É por isso que o FindBugs sempre sugere a alteração de classes internas anônimas para staticclasses aninhadas nomeadas , se elas não usarem sua thisreferência implícita .

Edit: Tom Hawtin - tackline diz que se a classe anônima for criada em um contexto estático (por exemplo, no mainmétodo), a classe anônima será de fato static. Mas o JLS discorda :

Uma classe anônima nunca é abstract(§8.1.1.1). Uma classe anônima é sempre uma classe interna (§8.1.3); nunca é static(§8.1.1, §8.5.1). Uma classe anônima é sempre implicitamente final(§8.1.1.2).

O Java Glossary de Roedy Green diz que o fato de classes anônimas serem permitidas em um contexto estático depende da implementação:

Se você deseja confundir aqueles que mantêm seu código, as wags descobriram javac.exepermitirão classes anônimas nos staticcódigos e staticmétodos init , mesmo que a especificação da linguagem indique que classes anônimas nunca o são static. Essas classes anônimas, é claro, não têm acesso aos campos de instância do objeto. Eu não recomendo fazer isso. O recurso pode ser extraído a qualquer momento.

Edit 2: O JLS realmente cobre contextos estáticos de forma mais explícita em §15.9.2 :

Deixe C ser a classe que está sendo instanciado, e deixe eu ser a instância que está sendo criado. Se C é uma classe interna, então eu posso ter uma instância que encerra imediatamente. A instância imediatamente encerrada de i (§8.1.3) é determinada da seguinte maneira.

  • Se C é uma classe anônima, então:
    • Se a expressão de criação da instância de classe ocorrer em um contexto estático (§8.1.3), então eu não tenho uma instância que encerre imediatamente.
    • Caso contrário, a instância que encerra imediatamente i é this.

Portanto, uma classe anônima em um contexto estático é aproximadamente equivalente a uma staticclasse aninhada, pois não mantém uma referência à classe envolvente, mesmo que tecnicamente não seja uma staticclasse.


19
+1 para FindBugs - todo desenvolvedor Java deve ter isso em sua compilação.
Andrew Duffy

13
Isso é muito lamentável, porque significa que você pode evitar essa sintaxe quase concisa por motivos de desempenho.
Thilo

2
O JLS 3rd Ed lida com o caso de classes internas em contextos estáticos. Eles não são estáticos no sentido JLS, mas são estáticos no sentido indicado na pergunta.
Tom Hawtin - tackline

6
Aqui está um exemplo de como é dependente da implementação: esse código é impresso trueusando javac (sun-jdk-1.7.0_10) e falseusando o compilador Eclipse.
Paul Bellora

1
@MichaelMyers Tentei simular os FindBugs me alertando sobre a criação de um Anonymous Inner sem usar a referência 'this', e nada acontece. Você pode demonstrar como o FindBugs o alerta como você disse no início de sua resposta? Basta colar um link ou o que for.
Thufir Hawat

15

Mais ou menos. Uma classe interna anônima criada em um método estático obviamente será efetivamente estática, pois não há fonte para isso.

Existem algumas diferenças técnicas entre classes internas em contextos estáticos e classes aninhadas estáticas. Se você estiver interessado, leia o JLS 3rd Ed.


Na verdade, eu retiro isso; o JLS discorda. java.sun.com/docs/books/jls/third%5Fedition/html/… : "Uma classe anônima é sempre uma classe interna; nunca é estática."
Michael Myers

1
estático em um sentido diferente do que está em questão.
Tom Hawtin - tackline

1
Eu adicionei um pouco de esclarecimento.
Tom Hawtin - tackline

15

Eu acho que há um pouco de confusão na nomenclatura aqui, que, reconhecidamente, é muito boba e confusa.

Como você os chama, esses padrões (e algumas variações com visibilidade diferente) são todos possíveis, Java normais e legais:

public class MyClass {
  class MyClassInside {
  }
}

public class MyClass {
  public static class MyClassInside {
  }
}

public class MyClass {
  public void method() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

public class MyClass {
  public static void myStaticMethod() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

Eles são atendidos na especificação do idioma (se você realmente se incomoda, consulte a seção 15.9.5.1 para saber o que está dentro do método estático).

Mas esta citação está completamente errada :

O javac.exe permitirá classes anônimas dentro do código init estático e métodos estáticos, mesmo que a especificação da linguagem indique que as classes anônimas nunca são estáticas

Eu acho que o autor citado está confundindo a palavra - chave estática com o contexto estático . (É certo que o JLS também é um pouco confuso a esse respeito.)

Honestamente, todos os padrões acima são bons (o que você chama de "aninhado", "interno", "anônimo" seja o que for ...). Realmente, ninguém removerá repentinamente essa funcionalidade no próximo lançamento do Java. Honestamente!


2
"(É certo que o JLS também é um pouco confuso a esse respeito.)" Você acertou. Parecia estranho dizer que depende da implementação, mas não me lembro de ter visto erros óbvios no Java Glossary antes. De agora em diante, tomo com um grão de sal.
Michael Myers

2
Na verdade, não estamos falando sobre nenhum dos padrões. Queremos dizer que a classe aninhada anônima é estática. Ou seja, adicione uma "estática" entre newe JComponentno seu terceiro exemplo.
Timmmm 21/09/12

Eu adicionei um esclarecimento à pergunta original para mostrar o que é desejado.
Timmmm 21/09/12

@MichaelMyers, o ditado no JLS sempre precisa ser interpretado.
Pacerier


0

classes internas anônimas nunca são estáticas (elas não podem declarar métodos estáticos ou campos estáticos não finais), mas se forem definidas em um contexto estático (método estático ou campo estático), elas se comportam como estáticas no sentido de que não podem acessar membros não estáticos (ou seja, instância) da classe envolvente (como todo o resto de um contexto estático)


-3

Na nota de tornar uma classe interna anônima estática chamando-a dentro de um método estático.

Na verdade, isso não remove a referência. Você pode testar isso tentando serializar a classe anônima e não tornando a classe anexa serializável.


5
-1: Criação de uma classe anónimo dentro de um método estático, na verdade, não remover a referência à classe externa. Você pode testar isso tentando serializar a classe anônima e não tornando a classe anexa serializável. (Eu apenas fiz.)
Christian Semrau
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.