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

142

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ê?

Rahul Garg
fonte
4
Porque agora Java é aquele velho COBOL :)
ses
A linha inferior é: porque eles ainda não o implementaram.
Intrepidis
1
'Interior não estático' é uma tautologia.
Marquês de Lorne
Se você não deseja expor sua classe interna a outras pessoas e esperar que ela contenha métodos estáticos, você pode colocar os modificadores "private" e "static" na classe interna.
Willie Z
Uma classe interna é por definição não estática. Você pode ter uma classe aninhada / membro estática e uma classe aninhada / membro não estática. Este último também é conhecido como classe interna. (Referência: seção 8.1.3 dos estados JLS "Uma classe interna é uma classe aninhada que não seja explicitamente ou implicitamente declarou static.")
Erwin Bolwidt

Respostas:

111

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.

Bill the Lizard
fonte
7
Sei que uma classe interna está associada a uma instância de sua classe externa e sei que é meio inútil que sejamos capazes de declarar membros estáticos dentro de uma classe interna, mas ainda estou perguntando por que uma classe interna não pode declarar membros estáticos?
Kareem
15
Em C ++, você pode ter, então isso é um bug na linguagem Java.
Antidepressivo industrial
39
A palavra bug ... Eu não acho que essa palavra significa o que você pensa que significa.
Seth Nelson
24
Uma frase mais apropriada seria 'irritante como mãe-camionista'. Não entenda por que o Java não permite isso. Às vezes, quero que uma classe interna use propriedades da classe pai, mas mantenha métodos estáticos para obter melhor espaço para nome. Existe algo inerentemente errado nisso? :(
Angad
6
Exatamente. Eu quero escrever uma classe interna de utilitário. Alguns de seus métodos se beneficiariam do acesso à classe externa, por isso não posso torná-la estática, mas alguns de seus métodos são apenas funções utilitárias. Por que não consigo ligar A.B.sync(X)ou até mesmo (de dentro de A) B.sync(x)?
Edward Falk
44

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 Innernã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.

Eddie
fonte
6
-1 Eu tenho que discordar do ângulo que você tomou aqui. Certamente podemos nos referir um tipo de classe interna, por exemplo 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.
Paul Bellora
2
Sim, classes internas podem declarar constantes estáticas . Isso não tem nada a ver com métodos estáticos! Embora você possa se referir a um método estático não estaticamente, isso é desencorajado. Todas as ferramentas de qualidade de código se queixam desse tipo de referência e por boas razões. E você perdeu o meu argumento. Eu nunca disse que não há como referenciar uma classe interna estática. Eu disse que não há maneira estática de referenciar o método estático de uma classe interna de uma classe externa não estática. Portanto, não há uma maneira ADEQUADA de referenciá-lo.
Eddie
25
"Não há muito sentido em permitir um método estático em uma classe interna não estática; como você o acessaria?" Você ligaria 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 Outerpara 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.
Paul Bellora
1
A diferença entre Outer.Inner.CONSTANTe Outer.Inner.staticMethod()é que uma referência a uma constante não tem chance de referenciar implicitamente a instância Outerem que Innerfoi instanciada. Todas as referências para Outer.staticMethod()compartilhar o mesmo estado exato. Todas as referências para Outer.Inner.CONSTANTcompartilhar 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.
Eddie
3
@ Eddie Você não pode se referir aos campos da instância em um método estático, portanto não há conflito relacionado à incapacidade de se referir ao campo da instância implícita 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.
Theodore Murdock
20

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 Innerclasse parece ter uma encarnação por Outerexemplo. 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$0nã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).

gustafc
fonte
7
Mas isso é apenas um erro do tipo "tentativa de acessar variável não estática a partir de um contexto estático" - não é diferente de se um método estático de nível superior tentar acessar sua variável de instância da própria classe.
Lawrence Dol
2
Gosto dessa resposta porque, na verdade, explica por que não é tecnicamente possível, mesmo que pareça possível sintaticamente.
LoPoBo
@gustafc, acho que foi uma excelente explicação. Mas, como Lawrence aponta, é apenas um fracasso por causa da referência a foo, que não é estática. Mas e se eu quisesse escrever public static double sinDeg(double theta) { ... }uma aula interna de matemática?
Edward Falk
6

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 Inneresteja associada a uma instância de Outer, ainda é possível usar Outer.Inner.myStaticpara referenciar um membro estático de Innerse o java decidir fazer isso.

Se você precisar compartilhar algo entre todas as instâncias Inner, basta colocá-los Outercomo membros estáticos. Isso não é pior do que você usa membros estáticos Inner, onde Outerainda pode acessar qualquer membro privado Inner(não melhora o encapsulamento).

Se você precisar compartilhar algo entre todas as instâncias Innercriadas por um outerobjeto, faz mais sentido colocá-las na Outerclasse 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.

Don Li
fonte
Classes internas também não são obrigatórias. No entanto, como a língua faz fornecer classes internas, ele deve fornecer uma implementação completa e significativa deles.
Intrepidis
4

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.

Benjamin Curtis Drake
fonte
3

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.

Russell Zahniser
fonte
Penso que uma dificuldade mais fundamental em permitir que classes internas não estáticas tenham membros estáticos não constantes é que os programadores que declaram que esses membros pretendem tê-los vinculados a instâncias da classe externa ou que sejam realmente estáticos. Nos casos em que uma construção - se legal - pode ser sensatamente especificada como significando uma de duas coisas diferentes, e onde essas duas coisas podem ser expressas de outras maneiras inequívocas, especificar essa construção como ilegal é geralmente melhor do que especificá-la como tendo qualquer significado.
Supercat
3

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:

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

É 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.

akhil_mittal
fonte
3

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.

  1. 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.

  2. 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.

Doomed93
fonte
2

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.

ESTAR
fonte
Benedikt, o que você quer dizer quando diz que "classes aninhadas estáticas são apenas um meio de agrupar"?
Ankur
0

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.

malaio
fonte
0

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.

Vishnu Saini
fonte
0

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 
user1888955
fonte
-1

É 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 ....

Creative_Cimmons
fonte
-2

Você tem métodos estáticos permitidos em classes aninhadas estáticas. Por exemplo

public class Outer {

  public static class Inner {

    public static void method() {

    }
  }
}
mR_fr0g
fonte