Esta pergunta é sobre se uma classe aninhada em Java deve ser uma classe aninhada estática ou uma classe aninhada interna. Pesquisei por aqui e no Stack Overflow, mas não consegui encontrar realmente nenhuma dúvida sobre as implicações de design dessa decisão.
As perguntas que encontrei estão perguntando sobre a diferença entre classes aninhadas estáticas e internas, o que é claro para mim. No entanto, ainda não encontrei um motivo convincente para usar uma classe aninhada estática em Java - com exceção das classes anônimas, que não considero para esta pergunta.
Aqui está minha compreensão do efeito do uso de classes aninhadas estáticas:
- Menos acoplamento: geralmente obtemos menos acoplamento, pois a classe não pode acessar diretamente os atributos de sua classe externa. Menos acoplamento geralmente significa melhor qualidade do código, testes mais fáceis, refatoração etc.
- Único
Class
: o carregador de classes não precisa cuidar de uma nova classe a cada vez, criamos um objeto da classe externa. Acabamos de receber novos objetos para a mesma classe repetidamente.
Para uma classe interna, geralmente considero que as pessoas consideram o acesso aos atributos da classe externa um profissional. Eu imploro para diferir nesse aspecto do ponto de vista do design, pois esse acesso direto significa que temos um alto acoplamento e, se quisermos extrair a classe aninhada em sua classe de nível superior separada, só podemos fazê-lo depois de transformar essencialmente em uma classe aninhada estática.
Portanto, minha pergunta se resume a isso: Estou errado ao supor que o acesso ao atributo disponível para classes internas não estáticas leva a um acoplamento alto, portanto a uma qualidade de código mais baixa, e deduzo disso que as classes aninhadas (não anônimas) devem geralmente ser estático?
Ou, em outras palavras: existe uma razão convincente para alguém preferir uma classe interna aninhada?
fonte
Respostas:
Joshua Bloch no Item 22 de seu livro "Effective Java Second Edition" diz quando usar que tipo de classe aninhada e por quê. Existem algumas citações abaixo:
Um uso comum de uma classe de membro estático é como uma classe auxiliar pública, útil apenas em conjunto com sua classe externa. Por exemplo, considere uma enumeração descrevendo as operações suportadas por uma calculadora. A operação enum deve ser uma classe de membro estática pública da
Calculator
classe. Os clientes deCalculator
poderiam então se referir a operações usando nomes comoCalculator.Operation.PLUS
eCalculator.Operation.MINUS
.Um uso comum de uma classe de membro não estático é definir um adaptador que permita que uma instância da classe externa seja vista como uma instância de alguma classe não relacionada. Por exemplo, as implementações da
Map
interface de tipicamente usar classes membro nonstatic para implementar as suas vistas de recolha , que são retornados porMap
'skeySet
,entrySet
evalues
métodos. Da mesma forma, implementações das interfaces de coleção, comoSet
eList
, geralmente usam classes de membros não estáticos para implementar seus iteradores:Se você declarar uma classe de membro que não requer acesso a uma instância anexa, sempre coloque o
static
modificador em sua declaração, tornando-a uma classe de membro estática em vez de não-estática.fonte
MySet
instâncias de classe externa ? Eu poderia imaginar algo como um tipo dependente de caminho sendo uma diferença, ou sejamyOneSet.iterator.getClass() != myOtherSet.iterator.getClass()
, mas, novamente, em Java isso realmente não é possível, pois a classe seria a mesma para cada um.Você está correto ao supor que o acesso ao atributo disponível para classes internas não estáticas leva a um acoplamento alto, portanto, a uma qualidade de código mais baixa e as classes internas (não anônimas e não locais ) geralmente devem ser estáticas.
As implicações da decisão de design para tornar a classe interna não estática são apresentadas no Java Puzzlers , Puzzle 90 (a fonte em negrito na citação abaixo é minha):
Se você estiver interessado, uma análise mais detalhada do Puzzle 90 é fornecida nesta resposta em Stack Overflow .
Vale ressaltar que acima é essencialmente uma versão estendida das orientações fornecidas no tutorial Java Classes and Objects :
Portanto, a resposta à pergunta que você fez em outras palavras, por tutorial é que a única razão convincente para usar non-static é quando é o acesso a campos e métodos não-públicas de uma instância encerrando necessário .
A redação do tutorial é um pouco ampla (esse pode ser o motivo pelo qual o Java Puzzlers tenta fortalecê-lo e reduzi-lo). Em particular, nunca foi realmente necessário acessar diretamente os campos da instância envolvente - no sentido de que maneiras alternativas como passar esses parâmetros como construtor / método sempre ficavam mais fáceis de depurar e manter.
No geral, meus (bastante dolorosos) encontros com a depuração de classes internas que acessam diretamente os campos da instância anexa causaram forte impressão de que essa prática se assemelha ao uso do estado global, juntamente com os males conhecidos associados a ele.
É claro que o Java faz com que os danos de um "quase global" fiquem contidos na classe envolvente, mas quando eu tive que depurar uma classe interna específica, parecia que um band-aid não ajudou a reduzir a dor: eu ainda tinha ter em mente a semântica e os detalhes "estrangeiros" em vez de se concentrar totalmente na análise de um objeto problemático em particular.
Por uma questão de integridade, pode haver casos em que o raciocínio acima não se aplique. Por exemplo, de acordo com minha leitura de javadocs map.keySet , esse recurso sugere um acoplamento rígido e, como resultado, invalida argumentos contra classes não estáticas:
Não que isso acima, de alguma forma, facilitaria a manutenção, o teste e a depuração do código envolvido, mas pelo menos poderia permitir que alguém argumentasse que a complicação corresponde / é justificada pela funcionalidade pretendida.
fonte
Alguns equívocos:
IIRC - Na verdade, pode. (É claro que, se forem campos de objetos, ele não terá um objeto externo para examinar. No entanto, se ele obtiver esse objeto de qualquer lugar, poderá acessar diretamente esses campos).
Não sei bem o que você quer dizer aqui. O carregador de classes está envolvido apenas duas vezes no total, uma vez para cada classe (quando a classe é carregada).
Divagam-se a seguir:
Em Javaland, parecemos um pouco assustados em criar classes. Eu acho que tem que parecer um conceito significativo. Geralmente pertence ao seu próprio arquivo. Precisa de clichê significativo. Precisa de atenção ao seu design.
Versus outras linguagens - por exemplo, Scala, ou talvez C ++: Não há absolutamente nenhum drama criando um pequeno suporte (classe, estrutura, qualquer que seja) a qualquer momento que dois bits de dados pertençam juntos. As regras de acesso flexível ajudam você a reduzir o padrão, permitindo manter o código relacionado fisicamente mais próximo. Isso reduz sua 'distância mental' entre as duas classes.
Podemos afastar as preocupações de design sobre o acesso direto, mantendo a classe interna em particular.
(... Se você perguntasse 'por que uma classe interna pública não estática ?', Essa é uma pergunta muito boa - acho que nunca encontrei um caso justificado para isso).
Você pode usá-los sempre que ajudar na legibilidade do código e evitar violações DRY e clichê. Não tenho um exemplo pronto, porque isso não acontece com tanta frequência na minha experiência, mas acontece.
Classes internas estáticas ainda são uma boa abordagem padrão , btw. Não estático possui um campo oculto extra gerado por meio do qual a classe interna se refere à externa.
fonte
Pode ser usado para criar um padrão de fábrica. Um padrão de fábrica é frequentemente usado quando os objetos criados precisam de parâmetros adicionais do construtor para funcionar, mas são tediosos para fornecer sempre:
Considerar:
Usado como:
O mesmo poderia ser alcançado com uma classe interna não estática:
Use como:
As fábricas se beneficiam de ter métodos nomeados especificamente, como
create
,createFromSQL
oucreateFromTemplate
. O mesmo pode ser feito aqui também na forma de várias classes internas:É necessária menos passagem de parâmetro da fábrica para a classe construída, mas a legibilidade pode sofrer um pouco.
Comparar:
vs
fonte