Por que as classes Java externas podem acessar membros privados da classe interna?

177

Observei que as classes externas podem acessar variáveis ​​de instância privada de classes internas. Como isso é possível? Aqui está um código de exemplo demonstrando o mesmo:

class ABC{
    class XYZ{
        private int x=10;
    }

    public static void main(String... args){
        ABC.XYZ xx = new ABC().new XYZ();
        System.out.println("Hello :: "+xx.x); ///Why is this allowed??
    }
}

Por que esse comportamento é permitido?

Harish
fonte
Esta questão me confundiu por um bom tempo até eu ver o comentário ... que explica por que eu não posso acesso xx.x na minha máquina ..
Wang Sheng
4
Os comentários me confundiram, eu executo o código acima no Java 8, ele compila e executa. xx.x pode ser acessado.
Leon
Harish, você pode não aceitar a resposta aceita (que não responde à pergunta que você fez) e, em vez disso, aceitar a resposta de Martin Andersson abaixo, que responde muito bem a ela?
temporary_user_name
FWIW essa sintaxe é absolutamente horrenda:new ABC().new XYZ()
Josh M.

Respostas:

80

A classe interna é apenas uma maneira de separar claramente algumas funcionalidades que realmente pertencem à classe externa original. Eles devem ser usados ​​quando você tiver 2 requisitos:

  1. Alguma parte da funcionalidade da sua classe externa seria mais clara se fosse implementada em uma classe separada.
  2. Embora esteja em uma classe separada, a funcionalidade está intimamente ligada à maneira como a classe externa funciona.

Dado esses requisitos, as classes internas têm acesso total à sua classe externa. Como eles são basicamente membros da classe externa, faz sentido que eles tenham acesso a métodos e atributos da classe externa - incluindo privados.

Kaleb Brasee
fonte
217
Esta resposta explica por que as classes aninhadas têm acesso a membros privados de sua classe externa. Mas a questão é por que a classe externa tem acesso a membros privados de classes aninhadas.
286 Andrew Andrew
13
Basta adicionar "e vice-versa" a Dados esses requisitos, as classes internas têm acesso total à sua classe externa e agora ela responde à pergunta.
Anthropomo #:
13
Esta não é a resposta correta para esse problema, aqui está: stackoverflow.com/questions/19747812/…
Colin Su
4
@anthropomo: Não, não. Ambos os requisitos são perfeitamente viáveis ​​sem que a classe externa tenha acesso a membros privados das classes internas.
OR Mapper
Um bom exemplo em que isso é particularmente útil é o Padrão do Construtor, stackoverflow.com/a/1953567/1262000 . A classe pai precisa apenas de um construtor que use um Construtor e acesse todas as suas variáveis ​​de membro. Caso contrário, você precisaria ter um construtor privado na classe pai com todas as variáveis ​​de membro privadas.
Vikky.rk
62

Se você deseja ocultar os membros privados de sua classe interna, você pode definir uma Interface com os membros públicos e criar uma classe interna anônima que implemente essa interface. Exemplo abaixo:

class ABC{
    private interface MyInterface{
         void printInt();
    }

    private static MyInterface mMember = new MyInterface(){
        private int x=10;

        public void printInt(){
            System.out.println(String.valueOf(x));
        }
    };

    public static void main(String... args){
        System.out.println("Hello :: "+mMember.x); ///not allowed
        mMember.printInt(); // allowed
    }
}
Ich
fonte
Este é um brilhante trecho de código. Apenas o que eu precisava. Obrigado!
Kevinarpe 8/03
Por favor, forneça o código que pode ser capaz de executar. O que é mais importante, por que razão a variável privada não pôde acessar.
Androidyue #
7
Mas então ... a classe interna é anônima. Você não pode criar várias instâncias dessa classe interna ou usar essa classe interna para quaisquer declarações de variáveis ​​etc.
OR Mapper
@OR Mapper é por isso que, mesmo que xseja público aqui, não é permitido mMember.x.
MAC
54

A classe interna é (para fins de controle de acesso) considerada parte da classe que o contém. Isso significa acesso total a todos os particulares.

A maneira como isso é implementado é usando métodos sintéticos protegidos por pacote: A classe interna será compilada em uma classe separada no mesmo pacote (ABC $ XYZ). A JVM não suporta esse nível de isolamento diretamente, de modo que, no nível do bytecode, ABC $ XYZ terá métodos protegidos por pacote que a classe externa usa para acessar os métodos / campos privados.

Thilo
fonte
17

Há uma resposta correta aparecendo em outra pergunta semelhante a esta: Por que o membro particular de uma classe aninhada pode ser acessado pelos métodos da classe envolvente?

Ele diz que há uma definição de escopo privado no JLS - Determinando a Acessibilidade :

Caso contrário, se o membro ou construtor for declarado privado, o acesso será permitido se, e somente se, ocorrer dentro do corpo da classe de nível superior (§7.6) que encerra a declaração do membro ou construtor.

Colin Su
fonte
Acho que esse trecho pode responder muito bem à minha pergunta.
shen
5

Um caso de uso importante da IMHO para classes internas é o padrão de fábrica. A classe envolvente pode preparar uma instância da classe interna sem restrições de acesso e passar a instância para o mundo externo, onde o acesso privado será respeitado.

Em contradição com a declaração abyx da classe static, não altera as restrições de acesso à classe envolvente, como mostrado abaixo. Além disso, as restrições de acesso entre classes estáticas na mesma classe anexa estão funcionando. Eu estava surpreso ...

class MyPrivates {
    static class Inner1 { private int test1 = 2; }
    static class Inner2 { private int test2 = new Inner1().test1; }

    public static void main(String[] args) {
        System.out.println("Inner : "+new Inner2().test2);
    }
}
thomasfr
fonte
1
Bom comentário, mas sem resposta.
ceving 04/09/2013
@ervers Esta é realmente a única resposta que me permitiu ver um uso real prático dessa decisão de design bizarra. A questão era por que foi decidido dessa maneira, e esse é um ótimo raciocínio - uma demonstração da diferença entre o que você pode querer que a classe externa acesse na classe interna e o que você deseja que outras classes não relacionadas acessem.
et_l 29/09/16
3

As restrições de acesso são feitas por classe. Não há como um método declarado em uma classe não poder acessar todos os membros da instância / classe. É lógico que as classes internas também tenham acesso irrestrito aos membros da classe externa, e a classe externa tenha acesso irrestrito aos membros da classe interna.

Ao colocar uma classe dentro de outra classe, você a está fortemente ligada à implementação, e qualquer coisa que faça parte da implementação deve ter acesso às outras partes.

TofuBeer
fonte
3

A lógica por trás das classes internas é que, se você criar uma classe interna em uma classe externa, é porque elas precisarão compartilhar algumas coisas e, portanto, faz sentido que elas possam ter mais flexibilidade do que as classes "regulares".

Se, no seu caso, não faz sentido que as classes possam ver o funcionamento interno uma da outra - o que basicamente significa que a classe interna poderia simplesmente ter se tornado uma classe regular, você pode declarar a classe interna como static class XYZ. Usar staticsignifica que eles não compartilharão o estado (e, por exemplo new ABC().new XYZ(), não funcionarão, e você precisará usá-lo new ABC.XYZ().
Mas, se for esse o caso, você deve pensar se XYZrealmente deve ser uma classe interna e que talvez mereça a sua). Às vezes, faz sentido criar uma classe interna estática (por exemplo, se você precisar de uma classe pequena que implemente uma interface que sua classe externa esteja usando e que não será útil em nenhum outro lugar) .Mas cerca da metade do tempo deveria ter sido feita uma classe externa.

abyx
fonte
2
A classe externa também pode acessar os membros privados de classes internas estáticas , portanto, isso não tem nada a ver com estática . Você diz "não faz sentido que as classes sejam capazes de ver o funcionamento interno uma da outra", mas esse não é necessariamente o caso - e se fizer sentido apenas para a classe interna ver o funcionamento interno da classe externa, mas não o vice- versa?
OR Mapper
3

Thilo adicionou uma boa resposta para sua primeira pergunta "Como isso é possível?". Desejo elaborar um pouco sobre a segunda pergunta: por que esse comportamento é permitido?

Para iniciantes, vamos deixar perfeitamente claro que esse comportamento não se limita às classes internas, que por definição são tipos aninhados não estáticos. Esse comportamento é permitido para todos os tipos aninhados, incluindo enumerações e interfaces aninhadas, que devem ser estáticas e não podem ter uma instância anexa. Basicamente, o modelo é uma simplificação da seguinte declaração: Código aninhado tem acesso total ao código anexo - e vice-versa.

Então porque? Eu acho que um exemplo ilustra melhor o ponto.

Pense no seu corpo e no seu cérebro. Se você injetar heroína em seu braço, seu cérebro fica alto. Se a região da amígdala do seu cérebro vir o que ele acredita ser uma ameaça à sua segurança pessoal, digamos uma vespa, por exemplo, ele fará seu corpo se virar e correr para as colinas sem você "pensar" duas vezes.

Assim, o cérebro é uma parte intrínseca do corpo - e estranhamente, o contrário também. O uso do controle de acesso entre essas entidades estreitamente relacionadas perde a reivindicação de relacionamento. Se você precisar de controle de acesso, precisará separar as classes mais em unidades verdadeiramente distintas. Até então, eles são a mesma unidade. Um exemplo de direção para estudos adicionais seria examinar como um Java Iteratorgeralmente é implementado.

O acesso ilimitado do código anexo ao código aninhado torna, na maioria das vezes, inútil adicionar modificadores de acesso aos campos e métodos de um tipo aninhado. Isso aumenta a confusão e pode fornecer uma falsa sensação de segurança para os novatos da linguagem de programação Java.

Martin Andersson
fonte
1
Essa deve ser realmente a resposta aceita. Tão claro e completo. Em vez disso, a resposta aceita nem sequer aborda a questão.
temporary_user_name
Na IMO, isso ainda não responde à pergunta de por que não podemos adicionar campos privados facilmente a uma classe interna, que a classe externa não pode acessar diretamente. A menos que eu esteja errado, isso arruina um dos principais casos de classes internas - a criação de tipos imutáveis ​​e de curta duração. Felizmente, o FWIW C # suporta isso: repl.it/repls/VengefulCheeryInverse
Josh M.
-1

A classe interna é considerada como um atributo da classe externa. Portanto, independentemente da variável de instância da classe Inner ser privada ou não, a classe Outer pode acessar sem nenhum problema, assim como acessar seus outros atributos privados (variáveis).

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}
MonMoonkey
fonte
-2

Porque seu main()método está na ABCclasse, que pode acessar sua própria classe interna.

aberrant80
fonte
2
A questão não é se os membros da ABCclasse podem acessar classes aninhadas na ABCclasse, mas por que eles podem acessar membros privados de classes aninhadas na ABCclasse em Java.
OR Mapper
Respondi à pergunta no mesmo dia em que foi feita. Alguém editou a pergunta 2 anos depois, e o voto negativo veio 3 anos depois. Tenho certeza de que quem editou a pergunta mudou muito o texto da pergunta.
aberrant80