Classe aninhada estática em Java, por quê?

217

Eu estava olhando para o código Java LinkedListe notei que ele fazia uso de uma classe aninhada estática Entry.

public class LinkedList<E> ... {
...

 private static class Entry<E> { ... }

}

Qual é o motivo do uso de uma classe aninhada estática, em vez de uma classe interna normal?

A única razão pela qual pude pensar foi que o Entry não tem acesso a variáveis ​​de instância, portanto, do ponto de vista do OOP, ele possui um melhor encapsulamento.

Mas pensei que poderia haver outras razões, talvez desempenho. O que poderia ser?

Nota. Espero que meus termos estejam corretos, eu chamaria de classe interna estática, mas acho que isso está errado: http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html

David Turner
fonte

Respostas:

271

A página da Sun à qual você vincula possui algumas diferenças importantes entre as duas:

Uma classe aninhada é um membro de sua classe anexa. Classes aninhadas não estáticas (classes internas) têm acesso a outros membros da classe envolvente, mesmo que sejam declaradas privadas. As classes aninhadas estáticas não têm acesso a outros membros da classe envolvente.
...

Nota: Uma classe aninhada estática interage com os membros da instância de sua classe externa (e outras classes) como qualquer outra classe de nível superior. Com efeito, uma classe aninhada estática é comportamentalmente uma classe de nível superior que foi aninhada em outra classe de nível superior por conveniência de empacotamento.

Não é necessário LinkedList.Entryhaver uma classe de nível superior, pois ela é usada apenasLinkedList (existem outras interfaces que também têm classes aninhadas estáticas nomeadas Entry, como Map.Entry- mesmo conceito). E como não precisa acessar os membros do LinkedList, faz sentido que seja estático - é uma abordagem muito mais limpa.

Como Jon Skeet aponta , acho que é uma idéia melhor se você estiver usando uma classe aninhada começar com a estática e decidir se realmente precisa ser não estática com base no seu uso.

matt b
fonte
Bah, eu não consigo obter um link de âncora para o comentário ao trabalho, mas a sua este comentário:#comment113712_253507
Zach Lysobey
1
@matt b Se uma classe aninhada estática não tem acesso aos membros da instância da classe Outer, como ela interage com os membros da instância da classe Outer?
39413 Geek
1
@mattb Mas como a @Geek percebeu, a página da Sun é contraditória: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class como isso é possível se apenas um parágrafo antes da documentação diz o seguinte: Static nested classes do not have access to other members of the enclosing class Talvez eles gostariam de dizer: A nested (non-static) class interacts with the instance members of its outer class (and other classes) just like any other top-level class
tonix
1
Obrigado David pelo link! Sim, eu estava errado, lendo meu comentário agora vejo que minha reformulação estava incorreta. Como você disse:, An inner class interacts with the instance members through an implicit reference to its enclosing classe isso aponta outra propriedade interessante de non-static inner classesas well anonymous inner classesor or local classes defined inside a block: todos eles não podem ter um no-argconstrutor, pois o compilador implicitamente precederá a sequência arg de cada construtor para passar uma referência de uma instância do anexo classe. Bem simples.
tonix 12/03/2015
1
você pode usar a classe interna estática para instanciar a classe externa que possui apenas construtor privado. Isso é usado no padrão do construtor. Você não pode fazer o mesmo com a classe interna.
seenimurugan
47

Na minha opinião, a pergunta deve ser inversa sempre que você vê uma classe interna - ela realmente precisa ser uma classe interna, com a complexidade extra e a referência implícita (em vez de IMO explícita e clara) a uma instância? da classe que contém?

Lembre-se, sou tendencioso como um fã de C # - C # não tem o equivalente a classes internas, embora tenha tipos aninhados. Não posso dizer que perdi as classes internas ainda :)

Jon Skeet
fonte
4
Eu posso estar errado, mas isso me parece um exemplo de uma classe aninhada estática, não uma classe interna. Eles até especificam no exemplo que não têm acesso a variáveis ​​de instância na classe circundante na classe aninhada.
ColinD
Sim, Colin está certo - o C # não tem classes internas, possui classes aninhadas. Lembre-se, uma classe aninhada estática em C # não é a mesma que uma classe aninhada estática em Java :)
Jon Skeet
2
Tipos aninhados são uma daquelas áreas em que o C # o corrigiu extremamente correto comparado ao Java. Eu sempre me maravilho com sua correção semântica / lógica ..
nawfal
3
@nawfal: Sim, exceto algumas imperfeições, estou admirado com o quão bem a linguagem C # foi projetada (e especificada).
Jon Skeet
1
@ JonSkeet, você tem um artigo ou blog sobre o que são essas imperfeições? Eu adoraria passar pelo que você encontra como "imperfeições" :)
Nawfal
27

Há problemas não óbvios de retenção de memória a serem considerados aqui. Como uma classe interna não estática mantém uma referência implícita à classe 'externa', se uma instância da classe interna for fortemente referenciada, a instância externa também será fortemente referenciada. Isso pode levar a um arranhão na cabeça quando a classe externa não é coletada como lixo, mesmo que pareça que nada faça referência a ela.

Leigh
fonte
Se a classe 'externa' é final e, portanto, não pode ser instanciada, esse argumento faz sentido nesse caso? Porque ter / manter uma referência a uma classe externa é inútil, se a última for final.
getsadzeg
10

Bem, por um lado, as classes internas não estáticas têm um campo extra e oculto que aponta para a instância da classe externa. Portanto, se a classe Entry não fosse estática, além de ter acesso desnecessário, levaria quatro ponteiros em vez de três.

Como regra, eu diria que, se você definir uma classe que está basicamente lá para atuar como uma coleção de membros de dados, como uma "estrutura" em C, considere torná-la estática.


fonte
8

A classe interna estática é usada no padrão do construtor. A classe interna estática pode instanciar sua classe externa, que possui apenas construtor privado. Portanto, você pode usar a classe interna estática para instanciar a classe externa que possui apenas construtor privado. Você não pode fazer o mesmo com a classe interna, pois é necessário que o objeto da classe externa seja criado antes de acessar a classe interna.

class OuterClass {
    private OuterClass(int x) {
        System.out.println("x: " + x);
    }

    static class InnerClass {
        public static void test() {
            OuterClass outer = new OuterClass(1);
        }
    }
}

public class Test {
    public static void main(String[] args) {
        OuterClass.InnerClass.test();
        // OuterClass outer = new OuterClass(1); // It is not possible to create outer instance from outside.
    }
}

Isso produzirá x: 1

seenimurugan
fonte
7

A classe aninhada estática é como qualquer outra classe externa, pois não tem acesso aos membros da classe externa.

Apenas por conveniência de empacotamento, podemos agrupar classes aninhadas estáticas em uma classe externa para fins de legibilidade. Fora isso, não há outro caso de uso da classe aninhada estática.

Exemplo para esse tipo de uso, você pode encontrar no arquivo Android R.java (recursos). A pasta Res do android contém layouts (contendo designs de tela), pasta drawable (contendo imagens usadas para o projeto), pasta values ​​(que contém constantes de string), etc.

Como todas as pastas fazem parte da pasta Res, a ferramenta Android gera um arquivo R.java (recursos) que contém internamente muitas classes aninhadas estáticas para cada uma de suas pastas internas.

Aqui está a aparência do arquivo R.java gerado no Android: Aqui eles estão usando apenas para conveniência de empacotamento.

/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.techpalle.b17_testthird;

public final class R {
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class layout {
        public static final int activity_main=0x7f030000;
    }
    public static final class menu {
        public static final int main=0x7f070000;
    }
    public static final class string {
        public static final int action_settings=0x7f050001;
        public static final int app_name=0x7f050000;
        public static final int hello_world=0x7f050002;
    }
}
user1923551
fonte
4

Exemplo simples:

package test;

public class UpperClass {
public static class StaticInnerClass {}

public class InnerClass {}

public static void main(String[] args) {
    // works
    StaticInnerClass stat = new StaticInnerClass();
    // doesn't compile
    InnerClass inner = new InnerClass();
}
}

Se não estática, a classe não pode ser instanciada, exceto em uma instância da classe superior (portanto, não no exemplo em que main é uma função estática)

Vinze
fonte
seu StaticInnerClass não é, de fato, uma classe aninhada / interna estática. é uma classe estática de nível superior.
theRiley
2

Uma das razões para estática versus normal tem a ver com carregamento de classe. Você não pode instanciar uma classe interna no construtor de seu pai.

PS: Eu sempre entendi 'aninhado' e 'interno' como intercambiáveis. Pode haver nuances sutis nos termos, mas a maioria dos desenvolvedores de Java também entenderia.

Mark Renouf
fonte
1

Classes internas não estáticas podem resultar em vazamentos de memória enquanto a classe interna estática irá protegê-las. Se a classe externa contiver dados consideráveis, poderá diminuir o desempenho do aplicativo.

A arma
fonte
'interior estático' é uma contradição em termos.
Marquês de Lorne,
1
@EJP, sheesh ... as pessoas realmente obter-off por apontar isto qualquer momento alguém menciona “classes internas estáticas” ...
Sakiboy
0

Eu não sei sobre diferença de desempenho, mas como você diz, a classe aninhada estática não faz parte de uma instância da classe envolvente. Parece mais simples criar uma classe aninhada estática, a menos que você realmente precise que ela seja uma classe interna.

É um pouco como por que eu sempre faço minhas variáveis ​​finais em Java - se elas não são finais, eu sei que há algo engraçado acontecendo com elas. Se você usar uma classe interna em vez de uma classe aninhada estática, deve haver um bom motivo.


fonte
Uma classe interna também não é 'parte de uma instância da classe envolvente'.
Marquês de Lorne
uma classe interna é existencialmente dependente da classe envolvente e possui acesso íntimo aos membros da classe envolvente; portanto, ela faz parte da classe envolvente. de fato, é um membro.
theRiley
0

Usar uma classe aninhada estática em vez de não estática pode economizar espaço em alguns casos. Por exemplo: implementando um Comparatordentro de uma classe, diga Aluno.

public class Student {
  public static final Comparator<Student> BY_NAME = new ByName();
  private final String name;
  ...
  private static class ByName implements Comparator<Student> {
    public int compare() {...}
  }
}

Então os staticgarante que a classe estudante tem apenas um comparador, em vez de instanciar um novo a cada vez que uma nova instância do estudante é criado.

JenkinsY
fonte
-1

Vantagem da classe interna

  1. uso único
  2. suporta e melhora o encapsulamento
  3. legibilidade
  4. acesso ao campo privado

Sem existir classe externa, a classe interna não existirá.

class car{
    class wheel{

    }
}

Existem quatro tipos de classe interna.

  1. classe interna normal
  2. Método Local Classe interna
  3. Classe interna anônima
  4. classe interna estática

ponto ---

  1. da classe interna estática, podemos acessar apenas membros estáticos da classe externa.
  2. Dentro da classe interna, não podemos declarar membro estático.
  3. para invocar a classe interna normal na área estática da classe externa.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  4. para invocar a classe interna normal na área de instância da classe externa.

    Inner i=new Inner();

  5. para invocar a classe interna normal fora da classe externa.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  6. inside Inner class Esse ponteiro para a classe interna.

    this.member-current inner class outerclassname.this--outer class

  7. para a classe interna, o modificador aplicável é - público, padrão,

    final,abstract,strictfp,+private,protected,static

  8. outer $ inner é o nome do nome da classe interna.

  9. classe interna dentro do método de instância, podemos acessar o campo estático e de instância da classe externa.

Classe 10.inner dentro do método estático, então podemos acessar apenas o campo estático de

classe externa.

class outer{

    int x=10;
    static int y-20;

    public void m1() {
        int i=30;
        final j=40;

        class inner{

            public void m2() {
                // have accees x,y and j
            }
        }
    }
}
Gaurav Tiwari
fonte