Por que não podemos ter um método estático em uma classe interna não estática?
Se eu tornar estática a classe interna, ela funcionará. Por quê?
static
.")
Por que não podemos ter um método estático em uma classe interna não estática?
Se eu tornar estática a classe interna, ela funcionará. Por quê?
static
.")
Respostas:
Como uma instância de uma classe interna está implicitamente associada a uma instância de sua classe externa, ela não pode definir nenhum método estático. Como uma classe aninhada estática não pode se referir diretamente a variáveis ou métodos de instância definidos em sua classe anexa, ela pode usá-los apenas por meio de uma referência a objeto, é seguro declarar métodos estáticos em uma classe aninhada estática.
A.B.sync(X)
ou até mesmo (de dentro de A) B.sync(x)
?
Não há muito sentido em permitir um método estático em uma classe interna não estática; como você acessaria? Você não pode acessar (pelo menos inicialmente) uma instância de classe interna não estática sem passar por uma instância de classe externa. Não existe uma maneira puramente estática de criar uma classe interna não estática.
Para uma classe externa Outer
, você pode acessar um método estático test()
como este:
Outer.test();
Para uma classe interna estática Inner
, você pode acessar seu método estático innerTest()
assim:
Outer.Inner.innerTest();
No entanto, se Inner
não for estático, agora não há uma maneira puramente estática de fazer referência ao método innertest
. Classes internas não estáticas estão vinculadas a uma instância específica de sua classe externa. Uma função é diferente de uma constante, na medida em que uma referência a Outer.Inner.CONSTANT
é garantida como inequívoca de uma maneira que uma função chamaOuter.Inner.staticFunction();
não é. Digamos que você tenha Inner.staticFunction()
essas chamadas getState()
, definidas em Outer
. Se você tentar invocar essa função estática, agora terá uma referência ambígua à classe Inner. Ou seja, em qual instância da classe interna você invoca a função estática? Importa. Veja, não há uma maneira verdadeiramente estática de referenciar esse método estático, devido à referência implícita ao objeto externo.
Paul Bellora está certo de que os designers de linguagem poderiam ter permitido isso. Eles teriam que impedir cuidadosamente qualquer acesso à referência implícita à classe externa nos métodos estáticos da classe interna não estática. Neste ponto, qual é o valor de ser uma classe interna se você não pode fazer referência à classe externa, exceto estaticamente? E se o acesso estático está bom, por que não declarar estática toda a classe interna? Se você simplesmente tornar a própria classe interna estática, não terá uma referência implícita à classe externa e não terá mais essa ambiguidade.
Se você realmente precisa de métodos estáticos em uma classe interna não estática, provavelmente precisará repensar seu design.
Outer.Inner i = new Outer().new Inner();
Além disso, classes internas são autorizados a declarar estáticos constantes de acordo com JLS §15.28.
Outer.Inner.staticMethod()
exatamente como pode acessar Outer.Inner.CONSTANT
. "Você não pode acessar ... uma instância de classe interna não estática sem passar por uma instância de classe externa." Por que você precisaria de uma instância? Você não precisa de uma instância Outer
para ligar Outer.staticMethod()
. Eu sei que isso é muito exigente, mas meu argumento é que não faz sentido enquadrar sua resposta dessa maneira. IMHO os designers de linguagem poderiam ter permitido se quisessem.
Outer.Inner.CONSTANT
e Outer.Inner.staticMethod()
é que uma referência a uma constante não tem chance de referenciar implicitamente a instância Outer
em que Inner
foi instanciada. Todas as referências para Outer.staticMethod()
compartilhar o mesmo estado exato. Todas as referências para Outer.Inner.CONSTANT
compartilhar o mesmo estado exato. No entanto, as referências a Outer.Inner.staticMethod()
são ambíguas: O estado "estático" não é realmente estático, devido à referência implícita à classe externa em cada instância de Inner
. Não existe uma maneira realmente inequívoca e estática de acessá-lo.
Outer.this
. Concordo com os designers de linguagem Java que não há razão para permitir métodos estáticos ou campos estáticos não finais nas classes internas, porque tudo em uma classe interna deve estar dentro do contexto da classe envolvente.
Eu tenho uma teoria que pode ou não estar correta.
Primeiro, você deve saber algumas coisas sobre como as classes internas são implementadas em Java. Suponha que você tenha essa classe:
class Outer {
private int foo = 0;
class Inner implements Runnable {
public void run(){ foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(); }
}
Quando você o compila, o bytecode gerado terá a aparência de que você escreveu algo como isto:
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 permitimos métodos estáticos em classes internas não estáticas, convém fazer algo assim.
class Outer {
private int foo = 0;
class Inner {
public static void incrFoo(){ foo++; }
}
}
... o que parece bastante razoável, pois a Inner
classe parece ter uma encarnação por Outer
exemplo. Mas, como vimos acima, as classes internas não estáticas são realmente apenas açúcar sintático para classes "internas" estáticas; portanto, 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 vai funcionar, pois this$0
não é estático. Isso explica por que métodos estáticos não são permitidos (embora você possa argumentar que você pode permitir métodos estáticos, desde que não façam referência ao objeto anexo) e por que você não pode ter campos estáticos não finais ( seria contra-intuitivo se instâncias de classes internas não estáticas de diferentes objetos compartilhassem "estado estático"). Também explica por que os campos finais são permitidos (desde que não façam referência ao objeto anexo).
public static double sinDeg(double theta) { ... }
uma aula interna de matemática?
O único motivo é "não obrigatório", então por que se preocupar em apoiá-lo?
Sintaticamente, não há razão para proibir uma classe 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 referenciar um membro estático de Inner
se o java decidir fazer isso.
Se você precisar compartilhar algo entre todas as instâncias Inner
, basta colocá-los Outer
como membros estáticos. Isso não é pior do que você usa membros estáticos Inner
, onde Outer
ainda pode acessar qualquer membro privado Inner
(não melhora o encapsulamento).
Se você precisar compartilhar algo entre todas as instâncias Inner
criadas por um outer
objeto, faz mais sentido colocá-las na Outer
classe como membros comuns.
Não concordo com a opinião de que "uma classe aninhada estática é basicamente apenas uma classe de nível superior". Eu acho que é melhor considerar realmente uma classe aninhada estática / classe interna como parte da classe externa, porque eles podem acessar os membros privados da classe externa. E membros da classe externa também são "membros da classe interna". Portanto, não há necessidade de oferecer suporte a membro estático na classe interna. Um membro comum / estático na classe externa será suficiente.
De: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Assim como os métodos e variáveis de instância, uma classe interna é associada a uma instância de sua classe envolvente e tem acesso direto aos métodos e campos desse objeto. Além disso, como uma classe interna está associada a uma instância, ela não pode definir nenhum membro estático.
A explicação da Oracle é superficial e ondulada. Como não há razão técnica ou sintática para antecipar membros estáticos dentro de uma classe interna (é permitido em outras linguagens como C #), a motivação dos designers de Java era provavelmente gosto conceitual e / ou uma questão de conveniência técnica.
Aqui está a minha especulação:
Diferentemente das classes de nível superior, as classes internas são dependentes da instância: uma instância da classe interna é associada a uma instância de cada uma de suas classes externas e tem acesso direto a seus membros. Essa é a principal motivação para tê-los em Java. Expressa de outra maneira: uma classe interna é destinada à instanciação no contexto de uma instância de classe externa. Sem uma instância de classe externa, uma classe interna não deve ser mais utilizável do que os outros membros da instância externa. Vamos nos referir a isso como o espírito dependente da instância das classes internas.
A própria natureza dos membros estáticos (que NÃO são orientados a objetos) entra em conflito com o dependente da instância espírito das classes internas (que é orientado a objetos) porque você pode fazer referência / chamar um membro estático de uma classe interna sem uma instância de classe externa, usando o nome qualificado da classe interna.
As variáveis estáticas, em particular, podem ofender de outra maneira: duas instâncias de uma classe interna associadas a instâncias diferentes da classe externa compartilhariam variáveis estáticas. Como as variáveis são um componente do estado, as duas instâncias da classe interna compartilhariam o estado independentemente das instâncias da classe externa às quais estão associadas. Não é inaceitável que variáveis estáticas funcionem dessa maneira (nós as aceitamos em Java como um comprometimento sensível à pureza do OOP), mas é possível que haja uma ofensa mais profunda ao permitir-lhes em classes internas cujas instâncias já estão associadas a instâncias de classe externa por design. Proibir membros estáticos dentro das classes internas em favor do espírito dependente da instância colhe o bônus adicional de impedir esta ofensa mais profunda da OOP.
Por outro lado, nenhuma ofensa é causada por constantes estáticas, que não constituem um estado significativo e, portanto, são permitidas. Por que não proibir constantes estáticas para obter a máxima consistência com o espírito dependente da instância ? Talvez porque as constantes não precisem ocupar mais memória do que o necessário (se forem forçadas a não-estáticas, serão copiadas para todas as instâncias da classe interna que sejam potencialmente inúteis). Caso contrário, não consigo imaginar o motivo da exceção.
Pode não ser um raciocínio sólido, mas na OMI faz o maior sentido da observação superficial da Oracle sobre o assunto.
Resposta curta: O modelo mental que muitos programadores têm de como o escopo funciona não é o modelo usado pelo javac. Combinar o modelo mais intuitivo exigiria uma grande mudança na maneira como o javac funciona.
A principal razão pela qual os membros estáticos nas classes internas são desejáveis é a limpeza do código - um membro estático usado apenas por uma classe interna deve viver dentro dele, em vez de ter que ser colocado na classe externa. Considerar:
class Outer {
int outID;
class Inner {
static int nextID;
int id = nextID++;
String getID() {
return outID + ":" + id;
}
}
}
Considere o que está acontecendo no getID () quando eu uso o identificador não qualificado "outID". O escopo em que esse identificador aparece é semelhante a:
Outer -> Inner -> getID()
Aqui, novamente, porque é assim que o javac funciona, o nível "Exterior" do escopo inclui membros estáticos e de instância do Exterior. Isso é confuso, porque geralmente nos dizem para pensar na parte estática de uma classe como outro nível do escopo:
Outer static -> Outer instance -> instanceMethod()
\----> staticMethod()
Dessa maneira, é claro que staticMethod () pode ver apenas membros estáticos de Outer. Mas se fosse assim que o javac funciona, a referência a uma variável de instância em um método estático resultaria em um erro "o nome não pode ser resolvido". O que realmente acontece é que o nome é encontrado no escopo, mas 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 a partir de um contexto estático.
OK, como isso se relaciona com classes internas? Ingenuamente, achamos que não há razão para que as classes 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 classe interna e declarações de instância na classe externa estão no escopo dentro do contexto de instância da classe interna, mas nenhuma delas é realmente aninhada na outra; ambos são aninhados no escopo estático de Outer.
Não é assim que o javac funciona - há um único nível de escopo para membros estáticos e de instância, e o escopo sempre é estritamente ninho. Até a herança é implementada copiando declarações para a subclasse em vez de ramificar e pesquisar no escopo da superclasse.
Para dar suporte a membros estáticos de classes internas, o javac precisaria dividir os escopos estáticos e de instância e oferecer suporte a hierarquias de escopo de ramificação e junção ou estender sua idéia simples de "contexto estático" booleano para mudar para rastrear o tipo de contexto em todos os níveis da classe aninhada no escopo atual.
Por que não podemos ter um método estático em uma classe interna não estática?
Nota: Uma classe aninhada não estática é conhecida como classe interna, portanto você não a possui non-static inner class
.
Uma instância de classe interna não existe sem uma instância correspondente da classe externa. Uma classe interna não pode declarar membros estáticos que não sejam constantes de tempo de compilação. Se fosse permitido, haveria ambiguidade sobre o significado de static
. Nesse caso, haveria certas confusões:
É por isso que os designers provavelmente decidiram não resolver esse problema.
Se eu tornar estática a classe interna, ela funcionará. Por quê ?
Novamente, você não pode tornar estática uma classe interna, mas pode declarar uma classe estática como aninhada. Nesse caso, essa classe aninhada é realmente parte da classe externa e pode ter membros estáticos sem nenhum problema.
Este tópico chamou a atenção de muitos, ainda tentarei explicar nos termos mais simples.
Primeiramente, com referência a http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 , uma classe ou interface é inicializada imediatamente antes da primeira ocorrência / invocação de qualquer membro precedido pela palavra-chave estática.
Portanto, se tolerarmos um membro estático dentro de uma classe interna, isso levará à inicialização da classe interna, não necessariamente da classe externa / envolvente. Portanto, dificultamos a sequência de inicialização da classe.
Considere também o fato de que uma classe interna não estática está associada à instância de uma classe envolvente / externa. Portanto, associar-se a uma instância significa que a classe interna existirá dentro de uma instância da classe Outer e será diferente entre as instâncias.
Simplificando o ponto, para acessar o membro estático, precisamos de uma instância de uma classe Exterior, a partir da qual precisaremos novamente criar uma instância de classe interna não estática. Os membros estáticos não devem estar vinculados a instâncias e, portanto, você recebe um erro de compilação.
Uma classe interna é algo completamente diferente de uma classe aninhada estática, embora ambas sejam similares em sintaxe. As classes aninhadas estáticas são apenas um meio de agrupamento, enquanto as classes internas têm uma forte associação - e acesso a todos os valores de - sua classe externa. Você deve ter certeza de por que deseja usar uma classe interna e, em seguida, deve ficar bem natural qual delas você deve usar. Se você precisar declarar um método estático, provavelmente é uma classe aninhada estática que você deseja.
Suponha que existam duas instâncias da classe externa e ambas instanciaram a classe interna. Agora, se a classe interna tiver um membro estático, ela manterá apenas uma cópia desse membro na área de heap. Nesse caso, os dois objetos da classe externa se referirão a esse cópia única e eles podem alterá-lo juntos. Isso pode causar a situação "Dirty read", portanto, para impedir que este Java aplique essa restrição. Outro ponto forte para apoiar esse argumento é que o java permite membros estáticos finais aqui, aqueles cujos valores não podem ser alterado de qualquer objeto de classe externa. Por favor, deixe-me se eu estiver errado.
Antes de tudo, por que alguém deseja definir o membro estático em uma classe interna não estática? A resposta é: para que o membro externo da classe possa usar esses membros estáticos apenas com o nome interno da classe, certo?
Mas, neste caso, podemos definir diretamente o membro na classe externa. que será associado a todos os objetos da classe interna, dentro da instância da classe 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 {
}
}
Portanto, na minha opinião, para não complicar o código, o java designer não está permitindo essa funcionalidade ou podemos vê-la em versões futuras com mais alguns recursos.
Tente tratar a classe como um campo normal, para que você entenda.
//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 classe interna como estáticos, porque você não poderá acessá-los em primeiro lugar.
Pense nisso: para acessar um membro estático, use className.memberName ,, no nosso caso, deve ser algo como outerclassName.innerclassName.memberName ,,, agora você vê por que a innerclass deve ser estática ....