Pergunta

Eu não entendo por que isso compila. f () e g () são visíveis a partir das classes internas, apesar de ser privado. Eles são tratados especial especialmente porque eles são classes internas?

Se A e B não são classes estáticas, ainda é o mesmo.

class NotPrivate {
    private static class A {
        private void f() {
            new B().g();
        }
    }

    private static class B {
        private void g() {
            new A().f();
        }
    }
}
Foi útil?

Solução

(Edit: expandiu a resposta para responder a algumas das observações)

O compilador leva as classes internas e os transforma em classes de nível superior. Desde métodos privados só estão disponíveis para a classe interna que o compilador tem que adicionar novos métodos "sintéticos" que têm acesso nível de pacote para que as classes de nível superior têm acesso a ele.

Algo como isso (os US $ são adicionados pelo compilador):

class A 
{
    private void f() 
    {
        final B b;

        b = new B();

        // call changed by the compiler
        b.$g();
    }

    // method generated by the compiler - visible by classes in the same package
    void $f()
    {
        f();
    }
}

class B
{
    private void g() 
    {
        final A a;

        a = new A();

        // call changed by the compiler
        a.$f();
    }

    // method generated by the compiler - visible by classes in the same package
    void $g()
    {
        g();
    }
}

classes não-estáticos são os mesmos, mas eles têm a adição de uma referência para a classe externa para que os métodos podem ser chamados nele.

A razão Java faz isso dessa maneira é que eles não querem requerem VM muda para suportar classes internas, então todas as mudanças tinham que estar no nível do compilador.

O compilador toma a classe interna e transforma-lo em uma classe de nível superior (assim, no nível de VM não existe tal coisa como uma classe interna). O compilador, em seguida, também tem de gerar os novos métodos "encaminhamento". Elas são feitas no nível do pacote (não pública) para garantir que apenas as classes no mesmo pacote podem acessá-los. O compilador também atualizou as chamadas de método para métodos privados para os métodos gerados "encaminhamento".

Você pode evitar que o compilador gerar o método minha declarar os métodos como "pacote" (a ausência de público, privado e protegido). A desvantagem para isso é que qualquer classe no pacote pode chamar os métodos.

Editar:

Sim, você pode chamar o método gerado (sintético), mas não faça isso:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Main
{
    public static void main(final String[] argv)
        throws Exception
    {
        final Class<?> clazz;

        clazz = Class.forName("NotPrivate$A");        

        for(final Method method : clazz.getDeclaredMethods())
        {
            if(method.isSynthetic())
            {
                final Constructor constructor;
                final Object instance;

                constructor = clazz.getDeclaredConstructor(new Class[0]);
                constructor.setAccessible(true);
                instance = constructor.newInstance();
                method.setAccessible(true);
                method.invoke(null, instance);
            }
        }
    }
}

Outras dicas

esta cotação resume muito bem :

... classes internas podem acessar todos os membros da classe declarante, mesmo os membros privados. Na verdade, a classe interna em si é dito ser um membro da classe; portanto, seguindo as regras de engenharia orientada a objetos, ele deve ter acesso a todos os membros da classe.

E após isso, já que ambas as classes internas são realmente apenas uma parte da classe que contém, eles devem ser capazes de acessar cada um dos outros membros privadas.

Java compila em assessores especiais com $ neles. Então você não pode escrever Java que acessar os métodos privados. Explicado aqui:

http://www.retrologic.com/innerclasses.doc7.html

Há mais uma categoria de membros gerados pelo compilador. Um membro particular m de uma classe C pode ser usado por outra classe D, se uma classe engloba a outra, ou se eles são fechados por uma classe comum. Desde que a máquina virtual não sabe sobre este tipo de agrupamento, o compilador cria um protocolo local de métodos de acesso em C para permitir D para ler, escrever, ou ligue para o membro m. Estes métodos têm nomes do acesso formulário de R $ 0, acessar $ 1, etc. Eles nunca são públicas. métodos de acesso são os únicos que podem ser adicionados às classes que encerram, classes não apenas internos.

Como usuário 'A Dude' explicou nos comentários da resposta aceita:

Ele compila, porque é necessário estar a trabalhar nesse sentido pela Specifation linguagem, ou seja. o Java Lang Spec diz assim:

6.6.1 Determinação de acessibilidade (pelo menos desde JLS6)

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

i. o "acesso de escopo" de um membro privado é:. em todos os lugares dentro dos limites lexicais do corpo da classe de nível superior

Isso significa que: todos os membros privados que são definidas dentro da classe de corpo da classe mais externa pode ser acessada de qualquer outro lugar nesta classe-corpo.

Por exemplo, o método particular de uma classe interna pode ser acedido a partir de métodos da classe exterior ou a partir de qualquer método de outra classe interna da classe externa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top