2010-04-25 34 views

Respuesta

11

Algunas clases internas están expuestas públicamente (p. Ej., Map.Entry en Java) pero esa es, con mucho, la excepción más que la norma.

Las clases internas son, básicamente, un detalle de implementación.

Por ejemplo, Swing hace un uso extensivo de las clases internas para los oyentes de eventos. Sin ellos, terminarías contaminando el espacio de nombres global con un montón de clases que de otra manera no necesitarías ver (lo que puede hacer que su propósito sea más difícil de determinar).

Las clases esencialmente internas son una forma de alcance. El acceso al paquete oculta las clases desde fuera del paquete. Las clases internas privadas ocultan esa clase desde fuera de esa clase.

Las clases internas en Java también son un sustituto de la falta de punteros de función o delegados de método (que están en C#) o cierres. Son el medio de pasar una función a otra función. Por ejemplo, en la clase Executor tiene:

void execute(Runnable r); 

lo que puede pasar un método en el En C/C++ que podría lograrse con:.

void execute(void (*run)()); 

ser un puntero a una función.

3

Las clases internas anónimas en Java son una forma de utilizar el patrón del adaptador.

interface Bar 
{ 
    public void bar(); 
} 

class Foo 
{ 
    public void foo() 
    { 
    // do something relevant 
    } 

    // it happens that foo() defines the same contract (or a compatible one) as 
    // Bar.bar(); with an anonymous inner class we can adapt Foo to the Bar 
    // interface 
    public Bar asBar() 
    { 
    // return an instance of an anonymous inner class that implements 
    // the Bar inteface 
    return new Bar() 
    { 
     public void bar() 
     { 
     // from an inner class, we can access the enclosing class methods 
     // as the "this pointers" are "linked" 
     foo(); 
     } 
    }; 
    } 
} 

En Java, asegúrese de que entiende el difference between inner classs and nested class:

una clase interna está asociado con un instancia de la clase envolvente y tiene acceso directo a métodos y campos

de ese objeto

C# no tiene clases internas en el sentido de Java, solo clases anidadas.

Ver este Inner Class Example.

2

Los uso para el alcance, por ejemplo, si tengo el libro electrónico de la clase y tengo ebookPrice, adjunto ebookPrice entre la clase de libros electrónicos, ya que está relacionada y solo se puede usar (al menos conceptualmente) dentro de ella.

ebookEl precio puede heredar de Price, que tiene un alcance más elevado y está relacionado con cualquier otra clase.

(solo mis dos centavos).

3

La mayoría de las veces que uso clases internas es porque las clases internas son lo más parecido al concepto de closure disponible en otros idiomas. Esto permite crear y trabajar con un objeto de ámbito anidado interno que tiene acceso a variables de su ámbito externo. Esto a menudo es útil para crear devoluciones de llamada (por ejemplo, definir varios Listener s en Swing), entre otras cosas.

3

Esta pieza de wikipedia podría ayudar a entender por qué necesitamos una clase interna:

Una instancia de una clase de nivel superior normal o puede existir en su propia . Por el contrario, una instancia de una clase interna no puede ser instanciada sin estar ligada a una clase de nivel superior.

Tomemos la noción abstracta de un automóvil con cuatro ruedas. Nuestras ruedas tienen una característica específica que depende de ser parte de nuestro automóvil. Esta noción no representa las ruedas como ruedas en una forma más general que podría ser parte del vehículo. En cambio, los representa como específicos de este. Podemos modelar esta noción usando clases internas de la siguiente manera:

Tenemos la clase de nivel superior de automóviles. Las instancias de Class Car se componen de cuatro instancias de la clase Wheel. Esta implementación particular de Wheel es específica del automóvil, por lo que el código no modela la noción general de Wheel que estaría mejor representada como una clase de nivel superior. Por lo tanto, está semánticamente conectado a la clase Car y el código de Wheel está de alguna manera acoplado a su clase externa.

Las clases internas nos proporcionan un mecanismo para modelar con precisión esta conexión. Decimos que nuestra clase de ruedas es Car.Wheel, Car es la clase de nivel superior y Wheel es la clase interna.

Las clases internas, por lo tanto, permiten la orientación del objeto de ciertas partes del programa que de otro modo no se encapsularían en una clase.

2

Hay idiomas que llevan las clases internas a un nivel bastante diferente, como Beta y Newspeak. En estos idiomas, la anidación de clases sirve como empaque (es decir, no hay paquetes).

Para obtener una buena cobertura de esta visión, consulte "How many concepts for modules do we need?" en el blog de equipos de objetos. Véase también el trabajo de Gilad Bracha en his blog ...

1

La orientado a objetos ventaja

En mi humilde opinión, la característica más importante de la clase interna es que le permite convertir las cosas en objetos que normalmente no se convertiría en objetos. Eso permite que su código esté aún más orientado a objetos de lo que sería sin clases internas.

Veamos la clase de miembro. Como su instancia es miembro de su instancia principal, tiene acceso a cada miembro y método en el padre. A primera vista, esto puede no parecer mucho; ya tenemos ese tipo de acceso desde dentro de un método en la clase padre. Sin embargo, la clase de miembro nos permite sacar la lógica del padre y objetivarla. Por ejemplo, una clase de árbol puede tener un método y muchos métodos de ayuda que realizan una búsqueda o caminata del árbol. Desde un punto de vista orientado a objetos, el árbol es un árbol, no un algoritmo de búsqueda. Sin embargo, necesita un conocimiento profundo de las estructuras de datos del árbol para realizar una búsqueda.

Una clase interna nos permite eliminar esa lógica y colocarla en su propia clase. Por lo tanto, desde un punto de vista orientado a objetos, hemos quitado la funcionalidad de donde no pertenece y la hemos incluido en su propia clase. Mediante el uso de una clase interna, hemos desacoplado con éxito el algoritmo de búsqueda del árbol. Ahora, para cambiar el algoritmo de búsqueda, simplemente podemos intercambiar una nueva clase. Podría continuar, pero eso abre nuestro código a muchas de las ventajas que ofrecen las técnicas orientadas a objetos.

La ventaja organizativa

diseño orientado a objetos no es cosa de todos, pero por suerte, las clases internas proporcionan más. Desde un punto de vista organizativo, las clases internas nos permiten organizar aún más nuestra estructura de paquetes mediante el uso de espacios de nombres. En lugar de descargar todo en un paquete plano, las clases se pueden anidar más dentro de las clases. Explícitamente, sin clases internas, estábamos limitados a la siguiente estructura jerárquica:

package1 
    class 1 
     class 2 
     ... 
     class n 
... 
package n 

Con las clases internas que podemos hacer lo siguiente:

package 1 
    class 1 
    class 2 
     class 1 
     class 2 
     ... 
     class n 

se usa con cuidado, las clases internas pueden proporcionar una jerarquía estructural que más naturalmente se adapta a tus clases.

La ventaja de devolución de llamada

clases miembro interior y clases anónimos ambos proporcionan un método conveniente para la definición de las devoluciones de llamada. El ejemplo más obvio se refiere al código GUI. Sin embargo, la aplicación de la devolución de llamada puede extenderse a muchos dominios.

La mayoría de las GUI Java tienen algún tipo de componente que instiga una llamada al método actionPerformed(). Desafortunadamente, la mayoría de los desarrolladores simplemente tienen su ventana principal implementada ActionListener. Como resultado, todos los componentes comparten el mismo método actionPerformed(). Para descubrir qué componente realizó la acción, normalmente hay un interruptor gigante y feo en el método actionPerformed().

He aquí un ejemplo de una aplicación monolítica:

public class SomeGUI extends JFrame implements ActionListener { 

    protected JButton button1; 
    protected JButton button2; 
    //... 
    protected JButton buttonN; 

    public void actionPerformed(ActionEvent e) { 
     if (e.getSource() == button1) { 
     // do something 
     } else if (e.getSource() == button2) { 
      //... you get the picture 
     } 
    } 
} 

Siempre que vea interruptores o grande si/si los bloques más, las alarmas ruidosas deben empezar a sonar en su mente. En general, tales constructos son malos diseños orientados a objetos, ya que un cambio en una sección del código puede requerir un cambio correspondiente en la instrucción de conmutación. Las clases de miembros internos y las clases anónimas nos permiten alejarnos del método actionPerformed() conmutado.

En su lugar, podemos definir una clase interna que implemente ActionListener para cada componente al que queremos escuchar. Eso puede resultar en muchas clases internas. Sin embargo, podemos evitar grandes declaraciones de cambio y tener la ventaja añadida de encapsular nuestra lógica de acción. Además, ese enfoque puede mejorar el rendimiento. En un cambio donde hay n comparaciones, podemos esperar n/2 comparaciones en el caso promedio. Las clases internas nos permiten establecer una correspondencia de 1: 1 entre el actor de acción y el oyente de acción. En una GUI grande, tales optimizaciones pueden tener un impacto sustancial en el rendimiento. Un enfoque anónima puede tener este aspecto:

public class SomeGUI extends JFrame { 
    // ... button member declarations ... 

    protected void buildGUI() { 
     button1 = new JButton(); 
     button2 = new JButton(); 
     //... 
     button1.addActionListener(
       new java.awt.event.ActionListener() { 
      public void actionPerformed(java.awt.event.ActionEvent e) { 
       // do something 
      } 
     }); 
// .. repeat for each button 
    } 
} 

Utilización de clases de elemento interior, el mismo programa se vería así:

public class SomeGUI extends JFrame 
{ 
    ... button member declarations ... 
    protected void buildGUI() 
    { 
     button1 = new JButton(); 
     button2 = new JButton(); 
     ... 
     button1.addActionListener(
     new java.awt.event.ActionListener() 
     { 
      public void actionPerformed(java.awt.event.ActionEvent e) 
      { 
       // do something 
      } 
     } 
    ); 
     .. repeat for each button 

Dado que las clases internas tienen acceso a todo en la matriz, podemos mover cualquier lógica que habría aparecido en una implementación monolítica actionPerformed() en una clase interna.

Prefiero usar clases de miembro como devoluciones de llamada. Sin embargo, esa es una cuestión de preferencia personal. Siento que demasiadas clases anónimas desordenan el código. También creo que las clases anónimas pueden volverse difíciles de manejar si son más grandes que una o dos líneas.

Desventajas?

Como en cualquier otra cosa, debes tomar lo bueno con lo malo. Las clases internas tienen sus desventajas. Desde el punto de vista del mantenimiento, los desarrolladores de Java sin experiencia pueden encontrar la clase interna difícil de entender. El uso de clases internas también aumentará la cantidad total de clases en tu código. Además, desde el punto de vista del desarrollo, la mayoría de las herramientas de Java son un poco insuficientes en cuanto a su compatibilidad con las clases internas. Por ejemplo, utilizo VisualAge para Java de IBM para mi codificación diaria. Mientras que las clases internas se compilarán dentro de VisualAge, no existe un buscador o plantilla de clase interna. En su lugar, simplemente debe escribir la clase interna directamente en la definición de la clase. Desafortunadamente, eso dificulta la exploración de la clase interna. También es difícil escribir, ya que pierde muchas ayudas de finalización de código de VisualAge cuando escribe en la definición de clase o utiliza una clase interna