Por que não podemos ter um método estático em uma class interna (não estática)?

Por que não podemos ter o método estático em uma class interna não estática?

Se eu fizer a class interna estática, funciona. Por quê ?

Como uma instância de uma class interna está implicitamente associada a uma instância de sua class externa, ela não pode definir nenhum método estático. Como uma class aninhada estática não pode se referir diretamente a variables ​​de instância ou methods definidos em sua class envolvente, ela pode usá-los somente por meio de uma referência de object, é seguro declarar methods estáticos em uma class aninhada estática.

Não há muito sentido em permitir um método estático em uma class interna não estática; como você acessaria? Você não pode acessar (pelo menos inicialmente) uma instância de class interna não estática sem passar por uma instância de class externa. Não existe uma maneira puramente estática de criar uma class interna não estática.

Para uma class externa Outer , você pode acessar um método de test() estático test() como este:

 Outer.test(); 

Para uma class interna estática Inner , você pode acessar seu método estático innerTest() assim:

 Outer.Inner.innerTest(); 

No entanto, se Inner não é estático, não existe agora uma maneira puramente estática de referenciar o método mais innertest . As classs internas não estáticas estão vinculadas a uma instância específica de sua class externa. Uma function é diferente de uma constante, em que uma referência a Outer.Inner.CONSTANT é garantida como não ambígua de uma maneira que uma chamada de function Outer.Inner.staticFunction(); não é. Digamos que você tenha Inner.staticFunction() que chama getState() , que é definido em Outer . Se você tentar invocar essa function estática, agora você tem uma referência ambígua à class Inner. Isto é, em qual instância da class interna você invoca a function estática? Importa. Veja, não existe uma maneira verdadeiramente estática de referenciar esse método estático, devido à referência implícita ao object externo.

Paul Bellora está certo de que os designers de linguagem poderiam ter permitido isso. Eles teriam então que desautorizar cuidadosamente qualquer access à referência implícita à class externa em methods estáticos da class interna não-estática. Neste ponto, qual é o valor para esta sendo uma class interna se você não pode referenciar a class externa, exceto estaticamente? E se o access estático é bom, por que não declarar toda a class interna estática? Se você simplesmente tornar a própria class interna estática, então você não tem nenhuma referência implícita à class externa, e você não tem mais essa ambigüidade.

Se você realmente precisar de methods estáticos em uma class interna não estática, provavelmente precisará repensar seu design.

Eu tenho uma teoria, que pode ou não estar correta.

Primeiro, você deve saber algumas coisas sobre como as classs internas são implementadas em Java. Suponha que você tenha essa class:

 class Outer { private int foo = 0; class Inner implements Runnable { public void run(){ foo++; } } public Runnable newFooIncrementer(){ return new Inner(); } } 

Quando você compila, o bytecode gerado parecerá como se você escrevesse algo assim:

 class Outer { private int foo = 0; static class Inner implements Runnable { private final Outer this$0; public Inner(Outer outer){ this$0 = outer; } public void run(){ this$0.foo++; } } public Runnable newFooIncrementer(){ return new Inner(this); } } 

Agora, se nós permitimos methods estáticos em classs internas não estáticas, você pode querer fazer algo assim.

 class Outer { private int foo = 0; class Inner { public static void incrFoo(){ foo++; } } } 

… o que parece razoável, já que a class Inner parece ter uma encarnação por instância Outer . Mas como vimos acima, as classs internas não-estáticas realmente são apenas açúcar sintático para classs “internas” estáticas, então o último exemplo seria aproximadamente equivalente a:

 class Outer { private int foo = 0; static class Inner { private final Outer this$0; public Inner(Outer outer){ this$0 = outer; } public static void incrFoo(){ this$0.foo++; } } } 

… o que claramente não funcionará, já que this$0 é não-estático. Esse tipo de explicação explica por que os methods estáticos não são permitidos (embora você possa argumentar que pode permitir methods estáticos, desde que eles não referenciem o object incluído), e por que você não pode ter campos estáticos não finais ( seria contra-intuitivo se instâncias de classs internas não-estáticas de objects diferentes compartilhassem “estado estático”. Também explica por que os campos finais são permitidos (desde que não façam referência ao object delimitador).

A única razão é “não é uma obrigação”, então por que se preocupar em apoiá-lo?

Sintaticamente, não há razão para proibir uma class interna de ter membros estáticos. Embora uma instância de Inner esteja associada a uma instância de Outer , ainda é possível usar Outer.Inner.myStatic para indicar um membro estático de Inner se java decidir fazê-lo.

Se você precisar compartilhar algo entre todas as instâncias do Inner , você pode simplesmente colocá-las no Outer como membros estáticos. Isso não é pior do que você usa membros estáticos no Inner , onde o Outer ainda pode acessar qualquer membro particular do Inner qualquer maneira (não melhora o encapsulamento).

Se você precisa compartilhar algo entre todas as instâncias de Inner criadas por um object outer , faz mais sentido colocá-las na class Outer como membros comuns.

Não concordo com a opinião de que “uma class aninhada estática é basicamente uma class de nível superior”. Eu acho que é melhor realmente considerar uma class interna / estática aninhada como parte da class externa, porque eles podem acessar membros privados da class externa. E membros da class externa são “membros da class interna” também. Portanto, não há necessidade de suportar o membro estático na class interna. Um membro normal / estático na class externa será suficiente.

Resposta curta: O modelo mental que a maioria dos programadores tem sobre como o escopo funciona não é o modelo usado pelo javac. Combinar o modelo mais intuitivo exigiria uma grande mudança na forma como o javac funciona.

A principal razão pela qual os membros estáticos em classs internas são desejáveis ​​é a limpeza do código – um membro estático usado apenas por uma class interna deve residir dentro dele, em vez de ter que ser colocado na class externa. Considerar:

 class Outer { int outID; class Inner { static int nextID; int id = nextID++; String getID() { return outID + ":" + id; } } } 

Considere o que está acontecendo em getID () quando eu uso o identificador não qualificado “outID”. O escopo em que esse identificador aparece é semelhante a:

 Outer -> Inner -> getID() 

Aqui, mais uma vez, porque é assim que funciona o javac, o nível “Externo” do escopo inclui membros estáticos e de instância do Outer. Isso é confuso porque geralmente nos dizem para pensar na parte estática de uma class como outro nível do escopo:

 Outer static -> Outer instance -> instanceMethod() \----> staticMethod() 

Nesse modo de pensar sobre isso, é claro que staticMethod () só pode ver membros estáticos de Outer. Mas se fosse assim que o javac funciona, referenciar uma variável de instância em um método estático resultaria em um erro “nome não pode ser resolvido”. O que realmente acontece é que o nome é encontrado no escopo, mas, em seguida, um nível extra de verificação entra em ação e descobre que o nome foi declarado em um contexto de instância e está sendo referenciado em um contexto estático.

OK, como isso se relaciona com as classs internas? Ingenuamente, achamos que não há razão para que as classs internas não possam ter um escopo estático, porque estamos imaginando o escopo funcionando assim:

 Outer static -> Outer instance -> Inner instance -> getID() \------ Inner static ------^ 

Em outras palavras, declarações estáticas na class interna e declarações de instância na class externa são ambas no escopo dentro do contexto da instância da class interna, mas nenhuma delas é realmente aninhada na outra; ambos estão nesteds no escopo estático de Outer.

Não é assim que funciona o javac – há um único nível de escopo para membros estáticos e de instância, e o escopo sempre é estritamente ninho. Até mesmo a inheritance é implementada copiando as declarações na subclass, em vez de ramificar e pesquisar o escopo da superclass.

Para suportar membros estáticos de classs internas, o javac teria que dividir escopos estáticos e de instância e suportar ramificações e junções de hierarquias de escopo, ou teria que estender sua idéia booleana de “contexto estático” para alterar o tipo de contexto em todos os níveis. da class aninhada no escopo atual.

Por que não podemos ter o método estático em uma class interna não estática?

Nota: Uma class aninhada não estática é conhecida como class interna, portanto, você não tem non-static inner class como tal.

Uma instância de class interna não tem existência sem uma instância correspondente da class externa. Uma class interna não pode declarar membros estáticos além das constantes de tempo de compilation. Se fosse permitido, haveria ambiguidade sobre o significado de static . Nesse caso, haveria certas confusões:

  1. Isso significa que há apenas uma instância na VM?
  2. Ou apenas uma instância por object externo?

É por isso que os designers provavelmente tomaram a decisão de não lidar com esse problema.

Se eu fizer a class interna estática, funciona. Por quê ?

Novamente, você não pode tornar uma class interna estática, mas pode declarar uma class estática como aninhada. Nesse caso, essa class aninhada é, na verdade, parte da class externa e pode ter membros estáticos sem nenhum problema.

Este tópico tem atraído a atenção de muitos, ainda vou tentar explicar nos termos mais simples.

Em primeiro lugar, com referência a http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 , uma class ou interface é inicializada imediatamente antes da primeira ocorrência / chamada de qualquer membro que é precedido pela palavra-chave estática.

  1. Então, se colocarmos um membro estático dentro de uma class interna, isso levará à boot da class interna, não necessariamente à class externa / abrangente. Então, nós dificultamos a seqüência de boot da class.

  2. Considere também o fato de que uma class interna não-estática está associada à instância de uma class delimitadora / externa. Portanto, associar-se a uma instância significa que a class interna existirá dentro de uma instância da class Outer e será diferente entre as instâncias.

Simplificando o ponto, para acessar o membro estático, precisamos de uma instância de uma class Outer, a partir da qual precisaremos criar novamente uma instância de class interna não estática. Membros estáticos não devem estar vinculados a instâncias e, portanto, você recebe um erro de compilation.

Uma class interna é algo completamente diferente de uma class aninhada estática, embora ambas sejam semelhantes na syntax. As classs aninhadas estáticas são apenas um meio de agrupamento, enquanto as classs internas têm uma forte associação – e access a todos os valores de – sua class externa. Você deve ter certeza de por que você quer usar uma class interna e então deve ser muito natural qual deles você tem que usar. Se você precisar declarar um método estático, provavelmente é uma class aninhada estática que você deseja de qualquer maneira.

suponha que haja duas instâncias da class externa e ambas instanciem a class interna. Agora, se a class interna tiver um membro estático, ela manterá apenas uma cópia desse membro na área de heap. Nesse caso, os dois objects da class externa se referirão a essa class. cópia única e eles podem alterá-lo juntos.Isso pode causar “Dirty read” situação, portanto, para evitar que este Java tenha aplicado esta restrição.Outro ponto forte para apoiar este argumento é que java permite membros estáticos finais aqui, aqueles cujos valores não podem ser mudou de qualquer object de class externa. Por favor, deixe-me se eu estiver errado.

Primeiro, por que alguém deseja definir o membro estático em uma class interna não estática? A resposta é, para que o membro da class externa possa usar esses membros estáticos somente com o nome da class interna, certo?

Mas, para este caso, podemos definir diretamente o membro na class externa. que será associado a todos os objects da class interna, dentro da instância da class externa.

como abaixo do código,

 public class Outer { class Inner { public static void method() { } } } 

pode ser escrito assim

 public class Outer { void method() { } class Inner { } } 

Então, na minha opinião, para não complicar o código, o designer java não está permitindo essa funcionalidade ou podemos ver essa funcionalidade em versões futuras com mais alguns resources.

Tente tratar a turma como um campo normal, então você entenderá.

 //something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class Outer.something 

É inútil ter membros da class interna como estáticos porque você não poderá acessá-los em primeiro lugar.

Pense nisso, para acessar um membro estático que você usa className.memberName, no nosso caso, deve ser algo como outerclassName.innerclassName.memberName, agora você vê porque innerclass deve ser estático ….

Você tem permissão para methods estáticos em classs aninhadas estáticas. Por exemplo

 public class Outer { public static class Inner { public static void method() { } } }