Pregunta Clase interna de Java y clase anidada estática


¿Cuál es la principal diferencia entre una clase interna y una clase anidada estática en Java? ¿El diseño / implementación juega un papel en la elección de uno de estos?


1464
2017-09-16 08:22


origen


Respuestas:


Desde el Tutorial de Java:

Las clases anidadas se dividen en dos categorías: estáticas y no estáticas. Las clases anidadas que se declaran estáticas simplemente se llaman clases anidadas estáticas. Las clases anidadas no estáticas se llaman clases internas.

Se accede a las clases anidadas estáticas utilizando el nombre de la clase adjunta:

OuterClass.StaticNestedClass

Por ejemplo, para crear un objeto para la clase anidada estática, use esta sintaxis:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Los objetos que son instancias de una clase interna existen dentro de una instancia de la clase externa. Considere las siguientes clases:

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

Una instancia de InnerClass solo puede existir dentro de una instancia de OuterClass y tiene acceso directo a los métodos y campos de su instancia adjunta.

Para crear una instancia de una clase interna, primero debe crear una instancia de la clase externa. Luego, crea el objeto interno dentro del objeto externo con esta sintaxis:

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

ver: Tutorial de Java - Clases anidadas

Para completar, tenga en cuenta que también existe algo así como clase interna sin una instancia adjunta:

class A {
  int t() { return 1; }
  static A a =  new A() { int t() { return 2; } };
}

Aquí, new A() { ... } es un clase interna definida en un contexto estático y no tiene una instancia adjunta.


1480
2017-09-16 08:28



los El tutorial de Java dice:

Terminología: las clases anidadas son   dividido en dos categorías: estático   y no estático. Clases anidadas que   se declaran estáticos simplemente se llaman   clases anidadas estáticas No estático   las clases anidadas se llaman internas   clases

En lenguaje común, los términos "anidado" e "interno" son usados ​​indistintamente por la mayoría de los programadores, pero usaré el término correcto "clase anidada" que abarca tanto interno como estático.

Las clases se pueden anidar indefinidamente, p.ej. la clase A puede contener la clase B que contiene la clase C que contiene la clase D, etc. Sin embargo, es raro encontrar más de un nivel de anidación de clases, ya que generalmente es un diseño incorrecto.

Hay tres razones por las que puede crear una clase anidada:

  • organización: a veces parece más sensato clasificar una clase en el espacio de nombres de otra clase, especialmente cuando no se utilizará en ningún otro contexto
  • acceso: las clases anidadas tienen acceso especial a las variables / campos de sus clases contenedoras (precisamente qué variables / campos dependen del tipo de clase anidada, ya sea interna o estática).
  • conveniencia: tener que crear un nuevo archivo para cada nuevo tipo es molesto, de nuevo, especialmente cuando el tipo solo se usará en un contexto

Existen cuatro tipos de clase anidada en Java. En resumen, son:

  • clase estática: declarado como miembro estático de otra clase
  • clase interna: declarado como un miembro de instancia de otra clase
  • clase interna local: declarado dentro de un método de instancia de otra clase
  • clase interna anónima: como una clase interna local, pero escrita como una expresión que devuelve un objeto único

Permítanme elaborar en más detalles.


Clases estáticas

Las clases estáticas son las más fáciles de entender porque no tienen nada que ver con instancias de la clase contenedora.

Una clase estática es una clase declarada como miembro estático de otra clase. Al igual que otros miembros estáticos, una clase así es simplemente una percha que usa la clase contenedora como su espacio de nombre, p.ej. la clase Cabra declarado como un miembro estático de la clase Rinoceronte en el paquete Pizza es conocido por el nombre pizza.Rhino.Goat.

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

Francamente, las clases estáticas son una característica bastante inútil porque las clases ya están divididas en espacios de nombres por paquetes. La única razón real concebible para crear una clase estática es que dicha clase tenga acceso a los miembros estáticos privados de su clase contenedora, pero considero que esta es una justificación poco convincente para que exista la característica de clase estática.


Clases internas

Una clase interna es una clase declarada como miembro no estático de otra clase:

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

    private void jerry() {
        Goat g = new Goat();
    }
}

Al igual que con una clase estática, la clase interna se conoce como calificada por su nombre de clase que contiene, pizza.Rhino.Goat, pero dentro de la clase contenedora, puede ser conocida por su nombre simple. Sin embargo, cada instancia de una clase interna está vinculada a una instancia particular de su clase contenedora: arriba, el Cabra creado en alemán, está implícitamente ligado a la Rinoceronte ejemplo esta en alemán. De lo contrario, hacemos que el asociado Rinoceronte instancia explícita cuando instanciamos Cabra:

Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();

(Observe que se refiere al tipo interno como solo Cabra en el extraño nuevo sintaxis: Java infiere el tipo que contiene del rinoceronte parte. Y si nuevo rinoceronte.Goat () también tendría más sentido para mí.)

Entonces, ¿qué nos gana esto? Bueno, la instancia de clase interna tiene acceso a los miembros de instancia de la instancia de clase contenedora. Estos miembros adjuntos de la instancia se mencionan dentro de la clase interna vía solo sus nombres simples, no vía  esta (esta en la clase interna se refiere a la instancia de clase interna, no a la instancia de clase que contiene asociada):

public class Rhino {

    private String barry;

    public class Goat {
        public void colin() {
            System.out.println(barry);
        }
    }
}

En la clase interna, puedes consultar esta de la clase contenedora como Rhino.thisy puedes usar esta para referirse a sus miembros, p.ej. Rhino.this.barry.


Clases internas locales

Una clase interna local es una clase declarada en el cuerpo de un método. Dicha clase solo se conoce dentro de su método contenedor, por lo que solo se puede instanciar y se puede acceder a sus miembros dentro de su método contenedor. La ganancia es que una instancia de clase interna local está vinculada y puede acceder a las variables locales finales de su método contenedor. Cuando la instancia utiliza un local final de su método contenedor, la variable conserva el valor que tenía en el momento de la creación de la instancia, incluso si la variable se ha salido del alcance (esta es efectivamente la versión limitada de cierres de Java).

Debido a que una clase interna local no es miembro de una clase o paquete, no se declara con un nivel de acceso. (Tenga en claro, sin embargo, que sus propios miembros tienen niveles de acceso como en una clase normal).

Si se declara una clase interna local en un método de instancia, una instanciación de la clase interna está vinculada a la instancia mantenida por el método contenedor esta en el momento de la creación de la instancia, y así los miembros de la instancia de la clase contenedora son accesibles como en una clase interna de instancia. Una clase interna local se instancia simplemente vía su nombre, p.ej. clase interna local Gato se instancia como nuevo Cat (), no es nuevo esto. Cat () como es de esperar.


Clases internas anónimas

Una clase interna anónima es una manera sintácticamente conveniente de escribir una clase interna local. Lo más común es que una clase interna local se instancia como máximo una vez cada vez que se ejecuta su método contenedor. Sería bueno, entonces, si pudiéramos combinar la definición de la clase interna local y su instanciación única en una forma de sintaxis conveniente, y también sería bueno si no tuviéramos que inventar un nombre para la clase (los menos inútiles) nombres que contiene tu código, mejor). Una clase interna anónima permite ambas cosas:

new *ParentClassName*(*constructorArgs*) {*members*}

Esta es una expresión que devuelve una nueva instancia de una clase sin nombre que se extiende ParentClassName. No puedes suministrar tu propio constructor; más bien, se proporciona implícitamente uno que simplemente llama al superconstructor, por lo que los argumentos suministrados deben ajustarse al superconstructor. (Si el padre contiene múltiples constructores, el más simple se llama "más simple", como lo determina un conjunto bastante complejo de reglas que no vale la pena preocuparse por aprender en detalle; solo preste atención a lo que NetBeans o Eclipse le dicen).

Alternativamente, puede especificar una interfaz para implementar:

new *InterfaceName*() {*members*}

Tal declaración crea una nueva instancia de una clase sin nombre que extiende Objeto e implementa InterfaceName. Nuevamente, no puedes suministrar tu propio constructor; en este caso, Java proporciona implícitamente un constructor no-arg, do-nothing (por lo que nunca habrá argumentos de constructor en este caso).

Aunque no puede dar un constructor a una clase interna anónima, aún puede hacer la configuración que desee utilizando un bloque de inicialización (un {} bloque colocado fuera de cualquier método).

Tenga en claro que una clase interna anónima es simplemente una forma menos flexible de crear una clase interna local con una instancia. Si desea una clase interna local que implemente múltiples interfaces o implemente interfaces mientras extiende alguna clase que no sea Objeto o que especifica su propio constructor, está atascado creando una clase interna local llamada con nombre.


524
2017-09-16 09:24



No creo que la diferencia real se haya aclarado en las respuestas anteriores.

Primero para obtener los términos correctos:

  • Una clase anidada es una clase que está contenida en otra clase en el nivel del código fuente.
  • Es estático si lo declaras con el estático modificador
  • Una clase anidada no estática se llama clase interna. (Me quedo con una clase anidada no estática).

La respuesta de Martin es correcta hasta ahora. Sin embargo, la pregunta real es: ¿Cuál es el propósito de declarar estática o no una clase anidada?

Tu usas clases anidadas estáticas si solo desea mantener sus clases juntas si pertenecen tópicamente juntas o si la clase anidada se utiliza exclusivamente en la clase adjunta. No hay diferencia semántica entre una clase anidada estática y cualquier otra clase.

Clases anidadas no estáticas son una bestia diferente Similar a las clases internas anónimas, estas clases anidadas son realmente cierres. Eso significa que capturan su alcance circundante y su instancia adjunta y la hacen accesible. Quizás un ejemplo aclarará eso. Ver este trozo de un contenedor:

public class Container {
    public class Item{
        Object data;
        public Container getContainer(){
            return Container.this;
        }
        public Item(Object data) {
            super();
            this.data = data;
        }

    }

    public static Item create(Object data){
        // does not compile since no instance of Container is available
        return new Item(data);
    }
    public Item createSubItem(Object data){
        // compiles, since 'this' Container is available
        return new Item(data);
    }
}

En este caso, desea tener una referencia de un elemento secundario al contenedor principal. Usando una clase anidada no estática, esto funciona sin algún trabajo. Puede acceder a la instancia adjunta de Container con la sintaxis Container.this.

Explicaciones más duras siguientes:

Si nos fijamos en los códigos de byte de Java que el compilador genera para una clase anidada (no estática), podría ser incluso más clara:

// class version 49.0 (49)
// access flags 33
public class Container$Item {

  // compiled from: Container.java
  // access flags 1
  public INNERCLASS Container$Item Container Item

  // access flags 0
  Object data

  // access flags 4112
  final Container this$0

  // access flags 1
  public getContainer() : Container
   L0
    LINENUMBER 7 L0
    ALOAD 0: this
    GETFIELD Container$Item.this$0 : Container
    ARETURN
   L1
    LOCALVARIABLE this Container$Item L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 1
  public <init>(Container,Object) : void
   L0
    LINENUMBER 12 L0
    ALOAD 0: this
    ALOAD 1
    PUTFIELD Container$Item.this$0 : Container
   L1
    LINENUMBER 10 L1
    ALOAD 0: this
    INVOKESPECIAL Object.<init>() : void
   L2
    LINENUMBER 11 L2
    ALOAD 0: this
    ALOAD 2: data
    PUTFIELD Container$Item.data : Object
    RETURN
   L3
    LOCALVARIABLE this Container$Item L0 L3 0
    LOCALVARIABLE data Object L0 L3 2
    MAXSTACK = 2
    MAXLOCALS = 3
}

Como puede ver, el compilador crea un campo oculto Container this$0. Esto se establece en el constructor que tiene un parámetro adicional de tipo Container para especificar la instancia adjunta. No puede ver este parámetro en la fuente pero el compilador lo genera implícitamente para una clase anidada.

El ejemplo de Martin

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

sería compilado a una llamada de algo así como (en bytecodes)

new InnerClass(outerObject)

En aras de la exhaustividad:

Una clase anónima es un ejemplo perfecto de una clase anidada no estática que simplemente no tiene ningún nombre asociado y no puede ser referenciada más tarde.


124
2017-09-16 09:12



Creo que ninguna de las respuestas anteriores explica la diferencia real entre una clase anidada y una clase anidada estática en términos del diseño de la aplicación:

Visión de conjunto

Una clase anidada podría ser no estático o estático y en cada caso es una clase definida dentro de otra clase. Una clase anidada debería existir solo para servir es clase adjunta, si una clase anidada es útil para otras clases (no solo para el adjunto), debe declararse como una clase de nivel superior.

Diferencia

Clase anidada no estática : está asociado implícitamente con la instancia adjunta de la clase contenedora, esto significa que es posible invocar métodos y acceder a variables de la instancia adjunta. Un uso común de una clase anidada no estática es definir una clase de Adaptador.

Clase estática anidada : no se puede acceder a la instancia de la clase adjunta e invocar métodos sobre ella, por lo que se debe usar cuando la clase anidada no requiera acceso a una instancia de la clase adjunta. Un uso común de la clase anidada estática es implementar componentes del objeto externo.

Conclusión

Entonces la principal diferencia entre los dos desde el punto de vista del diseño es: la clase anidada no estática puede acceder a la instancia de la clase contenedora, mientras que la estática no puede.


82
2018-05-27 07:43



En términos simples, necesitamos clases anidadas principalmente porque Java no proporciona cierres.

Las clases anidadas son clases definidas dentro del cuerpo de otra clase adjunta. Son de dos tipos: estáticos y no estáticos.

Se tratan como miembros de la clase adjunta, por lo tanto, puede especificar cualquiera de los cuatro especificadores de acceso: private, package, protected, public. No tenemos este lujo con las clases de alto nivel, que solo pueden declararse public o paquete privado

Las clases internas, también conocidas como clases no apiladas, tienen acceso a otros miembros de la clase superior, incluso si se declaran privadas, mientras que las clases anidadas estáticas no tienen acceso a otros miembros de la clase superior.

public class OuterClass {
    public static class Inner1 {
    }
    public class Inner2 {
    }
}

Inner1 es nuestra clase interna estática y Inner2 es nuestra clase interna que no es estática. La diferencia clave entre ellos, no puedes crear un Inner2 instancia sin un exterior donde como puede crear un Inner1 objeto de forma independiente.

¿Cuándo usarías la clase interna?

Piensa en una situación donde Class A y Class B están relacionados, Class B necesita acceder Class A miembros, y Class B está relacionado solo con Class A. Las clases internas entran en escena

Para crear una instancia de clase interna, necesita crear una instancia de su clase externa.

OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = outer.new Inner2();

o

OuterClass.Inner2 inner = new OuterClass().new Inner2();

¿Cuándo usarías la clase interna estática?

Definiría una clase interna estática cuando sepa que no tiene ninguna relación con la instancia de la clase / clase superior adjunta. Si su clase interna no utiliza métodos o campos de la clase externa, es solo una pérdida de espacio, por lo que es estático.

Por ejemplo, para crear un objeto para la clase anidada estática, use esta sintaxis:

OuterClass.Inner1 nestedObject = new OuterClass.Inner1();

La ventaja de una clase anidada estática es que no necesita un objeto de la clase contenedora / clase superior para funcionar. Esto puede ayudarlo a reducir el número de objetos que crea su aplicación en tiempo de ejecución.


30
2017-10-17 22:35



Creo que la convención que generalmente se sigue es la siguiente:

  • clase estática dentro de una clase de nivel superior es una clase anidada
  • clase no estática dentro de una clase de nivel superior es una clase interna, que más tiene dos formas más:
    • clase local - clases nombradas declaradas dentro de un bloque como un método o cuerpo constructor
    • clase anónima - clases sin nombre cuyas instancias se crean en expresiones y enunciados

Sin embargo, pocos otros puntos para recordar son:

  • Las clases de nivel superior y las clases estáticas estáticas son semánticamente las mismas, excepto que en el caso de una clase anidada estática puede hacer referencia estática a campos / métodos estáticos privados de su clase Outer [parent] y viceversa.

  • Las clases internas tienen acceso a variables de instancia de la instancia adjunta de la clase Outer [parent]. Sin embargo, no todas las clases internas tienen instancias adjuntas, por ejemplo, las clases internas en contextos estáticos, como una clase anónima utilizada en un bloque de inicializador estático, no lo hacen.

  • La clase anónima de forma predeterminada amplía la clase padre o implementa la interfaz padre y no hay ninguna cláusula adicional para extender cualquier otra clase o implementar más interfaces. Asi que,

    • new YourClass(){};medio class [Anonymous] extends YourClass {}
    • new YourInterface(){};   medio class [Anonymous] implements YourInterface {}

Siento que la pregunta más grande que permanece abierta ¿cuál usar y cuándo? Bueno, eso depende principalmente de la situación con la que estés lidiando, pero leer la respuesta dada por @jrudolph puede ayudarte a tomar una decisión.


24
2017-12-16 11:38



Aquí hay diferencias clave y similitudes entre la clase interna de Java y la clase anidada estática.

¡Espero eso ayude!

Clase interna

  • Puede acceder a la clase externa tanto instancia como estática métodos y campos
  • Asociado con la instancia de clase adjunta así que para crear una instancia primero necesita una instancia de clase externa (nota nuevo lugar de palabra clave):

    Outerclass.InnerClass innerObject = outerObject.new Innerclass();
    
  • No poder definir cualquier miembros estáticos sí mismo

  • No poder tener Clase o Interfaz declaración

Clase anidada estática

  • No puede acceder clase externa ejemplo métodos o campos

  • No asociado con ninguna instancia de clase adjunta Entonces para instanciarlo:

    OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
    

Similitudes

  • Ambos Clases internas puede acceder incluso campos privados y métodos de clase externa
  • También el Clase exterior tener acceso a campos privados y métodos de clases internas
  • Ambas clases pueden tener un modificador privado, protegido o de acceso público

¿Por qué usar clases anidadas?

Según la documentación de Oracle, hay varias razones (documentación completa)

  • Es una forma de agrupar lógicamente clases que solo se usan en un solo lugar: Si una clase es útil solo para otra clase, entonces es lógico incluirla en esa clase y mantener las dos juntas. Anidar tales "clases de ayuda" hace que su paquete sea más eficiente.

  • Aumenta la encapsulación: Considere dos clases de nivel superior, A y B, donde B necesita acceso a los miembros de A que, de lo contrario, serían declarados privados. Al ocultar la clase B dentro de la clase A, los miembros de A pueden declararse privados y B puede acceder a ellos. Además, B puede ocultarse del mundo exterior.

  • Puede conducir a un código más legible y mantenible: Anidar pequeñas clases dentro de las clases de nivel superior coloca el código más cerca de donde se usa.


23
2017-12-31 20:53