Clases internas en java

Clase interna que significa
Como su nombre indica, una clase dentro de otra clase se llama Clase Interna.
Las clases internas comparten una relación especial con su clase contenedora, ya que tiene acceso a todos los miembros de la clase externa(incluso a los private).
Esto se debe a que una clase interna también es un miembro de la clase externa al igual que otros campos y métodos.
Dado que una clase interna es una clase dentro de otra clase, también puede ser referida como una clase anidada.
Tipos de clases internas
Una clase interna puede ser de los siguientes tipos.

  1. Clase interna normal, Regular o anidada(normalmente denominada simplemente clase interna)
  2. Método Clase interna local.
  3. Clase interna anónima.
  4. Clase anidada estática

1. Clase interna anidada
Esta sección profundizará en todos los detalles de una clase interna. Para simplificar, considere las siguientes clases

class Outer { private String outerField = "Outer"; class Inner { private String innerField = "Inner"; }}

Aquí Exterior es la clase externa e Interior es la clase interna que está contenida dentro de Exterior.

Crear objeto de clase interna
Una clase interna está dentro de otra clase. La clase contenedora se llama clase externa. Por lo tanto, una clase interna no puede existir sin una clase externa y lo mismo se aplica a su objeto(o instancia).
Un objeto de clase interna no puede existir sin un objeto de clase externa.
Esto significa que para crear una instancia de clase interna, necesita tener una instancia de clase externa.
Por lo tanto, el objeto de clase interna se puede crear mediante el método indicado a continuación.

// first, create object of outer classOuter outerObject = new Outer();// create object of inner classInner innerObject = outerObject.new Inner();

Como se desprende del ejemplo, para crear un objeto de clase interna, es necesario un objeto de clase externa.
También hay un método de mano corta para crear objetos de clase interna.

Inner innerObject = new Outer().new Inner();

Este método también requiere un objeto de clase externa.
Solo Remember….An el objeto de clase interna no se puede crear sin un objeto de su clase contenedora (o externa).

Crear un objeto de clase interna Dentro de Clase Externa
El ejemplo anterior asume que está creando una instancia de clase interna desde algún lugar fuera de la clase externa.
Pero, en la mayoría de los casos, se requiere un objeto de clase interna solo dentro de la clase externa, ya que la clase externa hace uso de la clase interna.
Entonces, ¿cómo creamos un objeto de clase interna desde la clase interna externa?
Considere la clase de muestra modificada a continuación.

class Outer { private String outerField = "Outer"; public void createInnerClassObject() { // create an object of inner class Inner innerObject = new Inner(); } class Inner { private String innerField = "Outer"; }}

Observe la línea Inner innerObject = new Inner();.
Anteriormente se decía que un objeto de clase interna no se puede crear sin un objeto de clase externa, pero el fragmento de código anterior lo está haciendo. Confundido !!!!
La sintaxis anterior es válida porque está escrita dentro de un método de clase externa.
Cuando este código se está ejecutando, ya hay una instancia de la clase externa presente, que está ejecutando el método.

Recuerde siempre, Para crear(o acceder) a un objeto de clase interna, debe haber una instancia de clase externa.

Para resumir,

  1. Cuando se crea un objeto de clase interna a partir de una clase externa, se puede crear directamente como Inner innerObject = new Inner();.
  2. Cuando se crea un objeto de clase interna desde fuera de la clase externa, debe crearse como Inner innerObject = new Outer().new Inner();.

Cuando se imprimen objetos de clase externa e interna, a continuación se muestra la salida generada:

Instancia de clase externa: Instancia de clase interna Externa@659e0bfd
: Outer Inner Inner@2a139a55

Referencia a instancias, campos y métodos de clase externa de la clase interna
Una clase interna también es un miembro de su clase contenedora al igual que otros campos y métodos.
Por lo tanto, puede acceder a otros campos y métodos de clase externa de la misma manera que otros campos y métodos acceden entre sí, directamente.
Esto es válido cuando se accede a los campos y métodos de la clase externa desde la clase interna.

Pero, cuando se refiere a la instancia de la clase externa, hay una diferencia. Para hacer referencia a la instancia actual de una clase, se utiliza la palabra clave this.
En el caso de la clase interna, el uso de this la clase interna interna se refiere a la instancia actual de la clase interna y no a su clase externa.
Para hacer referencia a la instancia de la clase externa de la clase interna, necesitamos agregar el nombre de la clase a la palabra clave this.

Por lo tanto, para hacer referencia a la instancia de clase externa de la clase interna, use la sintaxis Outer.this.
Consulte el siguiente ejemplo para comprender.

class Outer { private String outerField = "Outer"; public void createInnerClassObject() { // create an object of inner class Inner innerObject = new Inner(); } class Inner { private String innerField = "Outer"; public innerClassMethod() { // access outer class field System.out.println("Outer field : " + outerField); // access inner class instance System.out.println("Inner instance : " + this); // access outer class instance System.out.println("Outer instance : " + Outer.this); } }}

Salida

Campo externo: Exterior
Instancia interna:
Instancia externa:

2. Método Clase Interna Local
Como su nombre indica, una clase definida dentro de un método es una Clase Interna Local del método.
Sí, es posible.
Puede definir una clase dentro de un método como se muestra a continuación.

// outer classclass Outer { // outer class method public void createClass() { // class inside a method class Inner { // inner class method public void innerMethod() { System.out.println("Method local inner class"); } } // inner class ends } // method ends}

El código anterior tiene una clase Externa que contiene un método.
Este método define una clase que está dentro del cuerpo del método, por lo tanto llamada clase interna local del método.
Observe que la clase interna también tiene su propia definición de método.

Crear instancias de un método clase interna local
El código anterior declara un método clase interna local pero no crea ningún objeto de clase interna.
Ahora surge la pregunta, cómo crear un objeto de clase interna local de método.
Dado que una clase interna local de método se define dentro de un método, es visible solo dentro de ese método y su objeto también se puede crear solo dentro de ese método.
El siguiente código crea un objeto de clase interna local de método y llama a su método.

// outer classpublic class Outer { // outer class method public void createClass() { // class inside a method class Inner { // inner class method public void innerMethod() { System.out.println("Method local inner class method called"); } } // inner class ends // create inner class object Inner innerObject = new Inner(); // call inner class method innerObject.innerMethod(); } // outer class method ends // Main method public static void main(String args) { // create object of outer class Outer outerObject = new Outer(); // call outer class method outerObject.createClass(); }}

Cuando se ejecuta el código anterior, a continuación se muestra la salida

Método de clase interna local llamado

Esto muestra que cuando se llama al método de clase externa, crea un objeto de clase interna y llama al método de clase interna.

Clase Interna local del método: Puntos para recordar
La instrucción que crea un objeto de clase interna local del método debe escribirse fuera del cuerpo de la clase.
Por lo tanto, la línea Inner innerObject = new Inner(); debe venir después de que termine la clase local.

  1. Una clase interna local de método solo se puede crear una instancia dentro del método en el que se declara.
    Esto tiene sentido ya que una clase de método es visible solo dentro del método, por lo que su objeto se puede crear solo dentro de ese método.
  2. Una clase interna local de método no puede acceder a las variables locales del método en el que se declara.
    Si necesita acceder a ellos, deben estar marcados como final.
    Esto es aplicable antes de java 8.
  3. No puede acceder a una variable local de método dentro de una clase interna local de método y luego reasignarla. Esto será un error del compilador.
    Esto es aplicable desde java 8 ya que las versiones anteriores a java 8 ni siquiera le permitirán acceder a una variable local de método dentro de la clase interna.
  4. Una clase interna local de método puede acceder a los campos y otros métodos de la clase a la que pertenece el método que contiene la clase interna.
  5. Una clase interna local de método puede acceder a los campos static y a otros métodos static de la clase a la que pertenece el método que contiene la clase interna solo cuando el método que contiene es static.

3. Clases internas anónimas
Anónimo significa cuyo nombre no se conoce. En el contexto de Java, una clase anónima es aquella que no tiene nombre.
El término Clase anónima solo se aplica a las clases internas, ya que las clases externas deben tener un nombre.
Una clase anónima es una clase interna porque siempre se definirá dentro de otra clase.

Tipos de clase anónima en java
En real, una clase anónima es una implementación de una clase ya existente o una interfaz que está escrita en otro lugar pero que se define de nuevo dentro de otra clase según sea necesario.
Esto puede sonar confuso, pero los ejemplos que siguen aclararán el concepto.

En función de si la clase anónima es una implementación de una clase o una interfaz, puede pertenecer a las siguientes dos categorías

A. Subclase de una clase
Comencemos con un ejemplo primero

// Already existing classclass Website {public void printName() {System.out.println("No website till now");}}class SearchEngine { // Notice the syntax Website w = new WebSite() { public void printName() { System.out.println("Website is codippa.com"); } };}

En el ejemplo anterior, hay una clase llamada Website que ya está creada.
Otra clase SearchEngine redefine esta clase, implementa su método y lo asigna a una variable de referencia que es del mismo tipo que la clase real.
También es posible llamar al método recién implementado usando esta variable de referencia.

Recuerde que la clase anónima recién implementada es una subclase de la clase real.
Sigue el polimorfismo y su objeto se puede pasar en cualquier lugar donde se espere el objeto de la clase de sitio web.
Vea el siguiente ejemplo para ilustrarlo.

class Website { public void printName() { System.out.println("No website till now"); }}class SearchEngine { // Notice the syntax Website w = new WebSite() { public void printName() { System.out.println("Website is codippa.com"); } }; // Expects an instance of Website class public void getWebsite(Website web) { // call the method of Website class web.printName(); } // Main method public static void main(String args) { // create an object of this class SearchEngine obj = new SearchEngine(); // call its method and pass instance of Website class obj.getWebsite(obj.w); }}

En lugar de definir previamente la implementación de la clase Website, se puede definir cuando sea necesario, es decir, mientras se llama al método getWebsite.
Por lo tanto, el método principal se puede modificar como

public static void main(String args) { SearchEngine obj = new SearchEngine(); // Notice the implementation of Website class as argument obj.getWebsite(new Website() { public void print() { System.out.println("Dynamic implementation"); } });}

La implementación anterior no se asigna a ninguna variable de referencia, no tiene nombre y, por lo tanto, el nombre Anónimo.

No es necesario que ambas clases estén en el mismo archivo o paquete. Pueden ubicarse en cualquier lugar en relación entre sí.

B. Implementador de Interfaz
Una clase anónima también se puede definir como el implementador de una interfaz.
En este caso, definirá la implementación de los métodos declarados en la interfaz y se puede pasar en cualquier lugar donde se espere el objeto de la interfaz.
Consulte el siguiente ejemplo para aclararlo.

interface WebInterface { // interface method declaration public void print();}class SearchEngine { // Notice the syntax WebInterface w = new WebInterface() { // Interface method implementation public void printName() { System.out.println("Website is codippa.com"); } }; // Expects an instance of WebInterface interface public void getWebsite(WebInterface web) { // call the method of WebInterface web.printName(); } // Main method public static void main(String args) { // create an object of this class SearchEngine obj = new SearchEngine(); // call its method and pass instance of WebInterface obj.getWebsite(obj.w); }}

Como antes, la implementación de la interfaz como clase anónima se puede crear cuando sea necesario, como se muestra a continuación.

public static void main(String args) { SearchEngine obj = new SearchEngine(); // Notice the implementation of WebInterface as argument obj.getWebsite(new WebInterface() { public void print() { System.out.println("Dynamic implementation"); } });}
Observe el uso de la palabra clave new antes del nombre de la interfaz. Clase anónima es el único lugar donde es posible.

Polimorfismo en clases anónimas
Como se indicó anteriormente, las clases anónimas siguen al polimorfismo.
Dado que son subclases, deben seguir polimorfismo. Esta sección detallará cómo.
Considere la siguiente clase.

class Metal { public void printThickness() { System.out.println("Thick enough"); }}class MetalDriver { // Anonymous class definition Metal metal = new Metal() { public void printThickness() { System.out.println("Thick enough"); } // new method public boolean hasLustre() { return false; } }; public void printMetalDetail(Metal m) { // call method present in actual class definition. No problem!!! m.printThickness(); // call newly defined method in Anonymous class. Compiler Error!!! m.hasLustre(); }}

El código anterior define un Metal de clase con un solo método.
Esta clase se implementa de nuevo de forma anónima en otra clase MetalDriver donde se agrega un nuevo método a su definición que no está presente en la clase real.

Ahora, cuando se llama a este nuevo método, el compilador se queja con un error

El método hasLustre () no está definido para el tipo Metal.

Esto se debe a que la clase anónima es una subclase, mientras que la referencia es de la clase real (que es la principal).
El compilador no pudo encontrar el nuevo método en la clase padre y marca un error.

Por lo tanto, el error establece dos hechos:
(i) La clase anónima es una subclase de la clase real, y
(ii) Las definiciones de clase anónima siguen el polimorfismo.

Uso de clase anónima
Supongamos que necesita llamar a un método que toma un argumento de un tipo que es una interfaz.
Ahora, esta interfaz pertenece a alguna biblioteca externa y no tiene una clase que implemente esta interfaz.
¿Cómo llamarás a este método? Si pasa null, existe el riesgo de excepciones en tiempo de ejecución.
Clase anónima al rescate.
Ahora puede crear una clase anónima que implemente esta interfaz y pasarla sobre el método que proporciona la implementación de sus métodos según sus necesidades.
A continuación se muestra el ejemplo.

// Interface from external Libraryinterface External { public void interfaceMethod();}class CalledClass { // Method to called. Takes an argument of interface type public void toBeCalled(External e) { e.interfaceMethod(); }}

El código anterior tiene una interfaz con un único método y una clase cuyo método necesita llamar desde su código.

El método de esta clase toma un argumento de tipo interfaz, pero no tiene una clase que implemente esta interfaz.
Si pasa null al argumento del método, lanzará inmediatamente un java.lang.NullPointerException.
Eche un vistazo a cómo puede llamar a este método usando la clase anónima.

class CallingClass { // Main method public static void main(String args) { CalledClass obj = new CalledClass(); // call method using Anonymous class obj.toBeCalled(new External() { public void interfaceMethod() { // your code } }); }}

Observe el uso de la implementación de interfaz anónima para llamar al método.
Este es el mayor beneficio de las clases anónimas.
4. Clase anidada estática
Una clase estática definida dentro de otra clase es una clase anidada estática. Se define como una clase normal precedida de una palabra clave estática.
Recuerde que no hay nada como clase estática, una clase anidada estática es solo un miembro estático de su clase externa.
Dado que es un miembro estático, lo siguiente se aplica a una clase anidada estática.

  1. Puede ser accedido por la clase externa directamente sin tener su instancia.
  2. Solo puede acceder a miembros estáticos de su clase externa, pero no a sus variables o métodos de instancia.

Ejemplo de clase anidada estática

public class Outer { // static nested class static class Inner { public void innerMethod() { System.out.println("Method of static nested class"); } } public void outerMethod() { System.out.println("Method of outer class"); } public static void main(String args) { // access inner class directly Inner inner = new Inner(); // call nested class method inner.innerMethod(); }}

Como puede ver en el ejemplo anterior, para acceder a clase anidada estática, no se requiere una instancia de clase externa y se puede acceder directamente a ella.
Tenga en cuenta que el objeto de la clase anidada estática no puede acceder a miembros no estáticos de la clase externa. En el ejemplo anterior, la instancia de clase anidada no puede invocar outerMethod de la clase externa.

Modifiquemos

  1. Cuando se compila una clase que contiene la clase interna, se generan 2 archivos de clase:
    uno para la clase externa y otro para la clase interna.
    Ejemplo, Exterior.clase y Exterior Inner interior.clase para clase externa Clase externa e interna clase interna respectivamente..
  2. El archivo de clase de la clase interna no se puede ejecutar directamente usando el comando java.
  3. Una clase interna normal no puede tener static miembros de datos o métodos.
  4. Una clase interna puede tener un constructor.
  5. Una clase local de método solo se puede crear instancias dentro del método en el que está definida.
  6. Una clase local de método definida en static method solo puede acceder a los miembros static de la clase que contiene.
  7. Una clase anónima que implementa una interfaz, puede implementar solo una interfaz en lugar de clases normales que pueden implementar muchas.
  8. Una clase anónima no puede extender una clase e implementar una interfaz al mismo tiempo que las clases normales.
  9. Las definiciones de clase anónimas terminan con}; (Observe el punto y coma).
  10. Clases anónimas que se definen como un argumento mientras se llama a un método end with}); (Observe el punto y coma).
  11. No puede llamar a un método en referencia de clase anónima que no esté presente en la clase real.
  12. Una clase interna precedida de static se conoce como clase anidada, no como clase interna.

Leave a Reply