Está en la página 1de 10

 

Clases anidadas e internas


SEMANA 1  
 

 
 
 
 

 
   
 

 
 
 
 

 
 
 

 
 
 
 

 
 
 

 
 

   

Aunque los casos en los que las clases internas y anidadas son útiles son bastante
puntuales y, en general, el uso de este tipo de figura no se recomienda por motivos de
buen diseño y legibilidad del código. Este es un tema presente en la arquitectura del
lenguaje y, por tanto, en los temas a evaluar en el examen. No se espera que
aparezcan muchas preguntas orientadas a evaluar exclusivamente su conocimiento
respecto a este tema, lo que se encontrará frecuentemente son preguntas que hacen
uso de clases internas o anidadas en los códigos que se presentan para formularlas.
Así que para tener todas las herramientas necesarias para analizar los códigos
presentados es necesario estudiar este tema (aquí se presentarán aspectos básicos, y
detalles que debe conocer al respecto).

Pueden identificarse, a grandes rasgos, cuatro tipos diferentes de clases anidadas:


clases internas normales, clases internas locales a métodos, clases anónimas y clases
internas estáticas.

Clases internas normales (Miembro)


Es tal vez el tipo de clase anidada más común en el desarrollo de una aplicación
habitual. Se declaran como un miembro más de la clase contenedora, junto con los
métodos y atributos. Por ejemplo, en:

class Contenedora{ 

  int atributo; 
  void metodo(){ 
//… 

class Interna{ 

//… 

}

Como a los demás miembros de la clase, a las clases internas miembro se les pueden
aplicar los modificadores de acceso normales: public, protected y private; además de

 
2  [ POLITÉCNICO GANCOLOMBIANO]
 

asignársele también un nivel de restricción por defecto (default) cuando no se


especifique explícitamente un modificador, como en el caso del ejemplo anterior. Las
repercusiones de esto vienen relacionadas con el lugar en donde son accesibles estas
clases internas, es decir, desde qué ámbitos se pueden instanciar estas clases, acceder
a sus miembros estáticos, o incluso heredar.

He aquí el principal problema de trabajar con este tipo de clases: las posibilidades para
trabajar con ellas pueden llevar a expresiones bastante extrañas en código fuente, que
no favorecen la mantenibilidad del código, por ser de difícil documentación y lectura.
Además de los modificadores de acceso, es también posible usar los modificadores:
final, abstract y strictfp (con los mismos efectos que resultarían al aplicarse sobre una
clase normal), y al tratarse esta vez de un miembro de la clase, también pude usarse el
modificador static, con efectos que serán discutidos en breve.

Para instanciar clases anidadas de este tipo, se debe proceder como normalmente se
hace con clases de cualquier tipo, lo único a tener en cuenta es el ámbito en el que se
realiza la instanciación, ya que si la instancia de la clase interna se quiere obtener fuera
de la clase contenedora, se deben cumplir ciertos requisitos. Siempre hay que tener en
cuenta que se trata de una clase interna, por tanto, no existe independientemente de su
clase contenedora. Si se desea crear la instancia dentro de algún método de la clase
contenedora, se puede utilizar la forma habitual para crear objetos, como si se tratara
de una clase cualquiera:
 
class Contenedora{ 
  public void metodoExter(){ 
Interna instancia = new Interna(); 
instancia.metodoInter(); 


class Interna{ 
public void metodoInter(){ 
System.out.println(“Se imprime”); 


 
3
 

Pero si se desea crear la instancia de la clase interna, desde fuera de la clase


contenedora se debe disponer primero de un objeto de la clase contenedora, desde el
cual se podrá referenciar a la clase interna. Es importante recordar siempre que así se
trate de una clase, esta al ser interna, es un miembro más de su clase contendora, y
para referenciar algún miembro de una clase, es necesario primero disponer de un
objeto de la clase. (No puedo sentarme en un asiento de un avión y volar en él a
Europa si sólo tengo en mis manos el plano del avión, más no el avión construido).
Veamos un ejemplo de este tipo de instanciación:

public class Otra{ 

  public static void main(String [] args){ 
      Externa exter = new Externa(); 
  Externa.Interna obj = exter.new Interna(); 
  obj.metodoInter(); 

La clase interna no es independiente, depende de su clase externa para ser


referenciada. Por esto, para declarar un objeto de esta clase se usa un nombre de clase
compuesto por los nombres de la clase externa e interna (Externa.Interna). Es
necesario tener un objeto concreto de la clase externa, para poder instanciar desde
éste a la clase interna. (exter.new Interna()).

Al ser un miembro más de la clase contendora, la clase interna tiene acceso a todos los
demás miembros de esta desde sus métodos. El uso de la palabra clave this cambia
radicalmente cuando se trata de clases internas. Como se sabe, this se usa siempre
para referenciar miembros de la clase. Al usarlo dentro de una clase anidada, this
igualmente referenciará miembros de la clase, pero siempre hay que tener en cuenta
que son los miembros de la clase interna los que se referenciaran no los de la clase
contenedora. Por ejemplo:
 
class Contenedora{ 
  public String atrib; 
  public Contenedora(String txt){ 

 
4  [ POLITÉCNICO GANCOLOMBIANO]
 

    this.atrib = txt; 


class Interna{ 
public void metodoInter(){ 
  System.out.println( this.atrib ); //Error 


}

Este código no compila. El uso de this en el constructor de Contenedora es correcto,


pues existe un miembro llamado atributo de tipo String, por tanto, la referencia y la
asignación son legales. Pero el uso de this en la clase interna no es legal, pues se está
intentando referenciar a un miembro llamado atrib, que no está definido para la clase
interna. El miembro de la clase externa se puede acceder desde la clase interna sin
problemas, pero el uso de this genera un error ya que atributo atrib es de la clase
Contendora no de Interna. Por tanto, para acceder al atributo, se puede prescindir del
uso de this, o modificar el uso de este para adaptarse al entorno de la clase interna.
Para corregir el error del ejemplo anterior, se puede reemplazar la línea problemática
por cualquiera de las siguientes dos sentencias, según lo expuesto:

System.out.println(atrib); 

ó
System.out.println( Contenedora.this.atrib ); 

Usando el this de esta forma, se puede acceder directamente a los miembros de la


clase contenedora si el uso de la palabra clave this se hace necesario, aunque siempre
es posible acceder a ellos prescindiendo de su uso.

Clases internas locales (internas a un método)


Es posible también definir una clase anidada que sea interna a un método de la clase
externa. En este caso, como en el caso de las variables locales, la clase solo podrá ser
accedida desde dentro del método. Desde cualquier otro ámbito, externo al método, la
clase no existe. La clase local, solo podrá ser instanciada o extendida en el interior del
método en donde se define. Si solamente puede ser accedida desde el interior del
método, no tiene sentido usar modificadores de acceso, el uso de estos es ilegal en

 
5
 

clases internas locales, solo es legal utilizar los modificadores abstract y final sobre este
tipo de clases. Veamos un ejemplo de una clase interna de este tipo:

class Contenedora{ 
  public String atrib; 

 
public void metodo(){ 
double local; 
class InternaLocal{ 
  private int valor; 

  public void metodoInterno(int v){ 
    this.valor = v;  
System.out.print(Contenedora.this.atrib); 


}

Como se puede ver en el ejemplo, dentro de una clase interna local es posible acceder
a los miembros de la clase contenedora, pero hay una restricción importante respecto a
las variables locales del método que la contiene: Una clase local a un método, no
puede acceder a las variables locales definidas en el método que la contiene, excepto
que estas variables sean marcadas con el modificador final. Igual sucede con los
parámetros del método si llegan a existir, solo pueden ser accedidos por la clase interna
local al método si estos parámetros son marcados como final:

public class Prueba { 
 

public String atrib; 
 
  public void metodo(final int parametro) { 

 
6  [ POLITÉCNICO GANCOLOMBIANO]
 

    double local; 

    class InternaLocal { 
      private int valor; 
 
      public void metodoInterno(int v) { 
        this.valor = v * parametro; 

        System.out.print(Prueba.this.atrib); 
        local++;  //Error de compilación 
      } 
    } 
  } 

El acceso al parámetro del método en este caso es legal, puesto que se ha marcado
como final, pero al intentar acceder a la variable local, se presenta un error de
compilación, ya que esta no utiliza el modificador final en su declaración. Experimente
en código fuente real las posibilidades y restricciones que se presentan al trabajar con
este tipo de clases.

Clases anónimas
Este tipo de clases genera gran confusión cuando se encuentran en un diseño pues su
estructura, poco convencional, torna confuso y no muy legible el código fuente. Lo
primero que rompe el esquema de las clases convencionales, es que este tipo de
clases no tienen nombre, pues se define en la misma línea de código en donde se crea
el objeto de la clase. Es como decirle al compilador, que se necesita un único objeto de
una clase, que presente ciertas características, pero que no interesa cómo se llame o
en dónde se encuentre, pues esta clase no volverá a ser utilizada en un ámbito
diferente al de definir el comportamiento y estado del objeto deseado. Estas clases, por
tanto, siempre deben ser definidas dentro de otra clase, por eso son consideradas
internas. Una clase anónima depende totalmente de la herencia para su definición. Las
clases anónimas necesitan utilizar una clase ya existente o una interfaz, ya que a partir
de estas es que se declara el objeto que contendrá la instancia de la clase anónima.
Veamos un ejemplo con un objeto de una clase anónima que modifica el
comportamiento de un método de su clase padre. Esta clase usa la clase del paquete
java.util llamada Vector, y sobrescribe el método add.

 
7
 

public class Prueba { 
  public String atrib; 

 
  public Vector obj = new Vector(){ 
     
    public synchronized boolean add(Object e) { 

      System.out.println("Metodo sobrescrito"); 
      return false; 
    } 
    public void otro(){ 
      System.out.println("Metodo Nuevo!"); 

    } 
  }; 

 

La clase anónima, en este caso, es aquella que extiende de la clase Vector para
redefinir el método add y definir el método otro. Esta clase no tiene nombre, sin
embargo, ya se tiene un objeto de esta clase y una referencia a este objeto ha sido
asignada a la variable de referencia obj. Esta variable se ha declarado como un objeto
de la clase padre de la clase anónima (Vector), pero no es un objeto de la clase Vector
directamente (aunque lo es por herencia), y no se puede esperar que se comporte
como tal. Se puede observar que la forma genérica de declarar e instanciar un objeto
de una clase anónima es la siguiente:

Super obj = new Super (){ 
//Definición 

};

 
8  [ POLITÉCNICO GANCOLOMBIANO]
 

Donde Super es la clase o interfaz que se desea extender o implementar para construir
la clase anónima. Definir una clase anónima e instanciar un objeto de ella, son
instrucciones que deben escribirse siempre en la misma línea de condigo, por eso el
punto y coma luego de la llave de cierre. Este es un aspecto clave ya que la definición
de la clase no es independiente sino que hace parte de la instrucción de asignación;
algunas preguntas del examen pueden recurrir a este hecho para generar confusión,
escribiendo definiciones de clases anónimas si utilizar el punto y coma, lo cual es un
error de compilación.
¿Qué hay del método llamado otro del código del ejemplo anterior? ¿Será posible
acceder a él fuera de la definición de la propia clase anónima? Si la respuesta no es
clara, un análisis detallado de las lecturas sobre programación orientada a objetos de
unidades posteriores es necesario. Ya que el objeto está declarado como un objeto de
la clase padre de la clase anónima, este solo podrá usarse para acceder a los
miembros y métodos que hayan sido heredados. Luego la definición del método otro
aunque legal, porque el compilador no presentará ningún error, es completamente inútil
en este ejemplo, pues el método jamás podrá ser utilizado desde afuera de la definición
de la clase anónima. El uso más frecuente de las clases anónimas, es al definir un
objeto que será pasado como argumento al llamado de un método. Esto lleva a
estructuras extrañas en el código fuente, e incluso incomprensibles si no se conoce
bien esta forma de trabajar con clases internas. Es común encontrar estas definiciones
cuando se trabaja con los controladores y componentes fuente de eventos en una
interfaz GUI, como tal vez ya haya trabajado:

JButton boton = new JButton("OK"); 
boton.addActionListener( new ActionListener(){ 
    public void actionPerformed(ActionEvent e){ 
        System.out.println("Acción"); 

      } 
    });

Clases internas estáticas


Son clases internas miembro de la clase que la contiene, que además tiene consigo en
su declaración el modificador static, que le da características especiales.
 

class Contenedora{ 
 

 
9
 

static class Interna{ 

//Contenido de la clase 

}

Al ser estáticas, este tipo de clases internas no pueden acceder a miembros no


estáticos de la clase contenedora. Esto se debe a que al ser estática, la clase interna
puede ahora ser referenciada sin necesidad de un objeto de la clase contenedora,
luego esta no puede entonces acceder a miembros que solo se pueden utilizar cuando
exista un objeto (miembros no estáticos). Para instanciar una clase interna estática
desde afuera de la clase contenedora, ahora es posible referenciar directamente esta
clase sin necesidad de un objeto, como en:

  Contenedora.Interna obj = new Contenedora.Interna(); 
 

Las clases internas estáticas, pueden tener miembros tanto estáticos como no estáticos,
así que puede complicarse aún más el acceso a miembros estáticos de clases internas
estáticas. De nuevo es importante la práctica para familiarizarse con estos conceptos,
las clases internas se pueden anidar indefinidamente, es decir, una clase puede ser
interna a otra que a su vez es interna, llegando a códigos bastante peculiares pero
igualmente legales. Compruébelo usted mismo, experimente en código fuente, diseños
que utilicen clases internas en todas sus expresiones y lleve a cabo múltiples niveles de
anidamiento, una vez construidas, escriba código que permita instanciar objetos de
estas clases si es posible, y acceder a sus miembros.

 
  

 
10  [ POLITÉCNICO GANCOLOMBIANO]

También podría gustarte