2010-02-17 28 views
20

En algunos de mis proyectos y en algunos libros se decía que no uso clase interna (anónimo o no, estática o no) - excepto en algunas condiciones restringidas, como EventListener s o s - Runnable es una buena práctica. Incluso fueron 'excluidos' en mi primer proyecto industrial.Clases internas de Java (anónimas o no): ¿es bueno usarlas?

¿Es esto realmente una mejor práctica? ¿Por qué?

(tengo que decir que los estoy usando un montón ...)

- EDITAR ---
no puedo escoger una respuesta correcta en todas estas respuestas: hay una parte de la rectitud en la mayoría de los casos: seguiré usando clases internas, ¡pero trataré de usarlas con menos frecuencia!

Respuesta

25

En Según mi punto de vista, el 90% de las clases internas en el código Java son entidades asociadas a una sola clase y, por lo tanto, fueron "incorporadas" como clases internas, o clases internas anónimas que existen porque Java no es compatible con Lambdas.

Personalmente, no me gusta ver clases internas complejas. Agregan complejidad al archivo fuente, lo hacen más grande, son feos de tratar en términos de depuración y creación de perfiles, etc. Me gusta separar mi proyecto en muchos paquetes, en cuyo caso puedo convertir a la mayoría de las entidades en clases de alto nivel. que están restringidos al paquete.

Eso me deja con las clases internas necesarias, como los oyentes de acción, la programación "funcional" falsa, etc. Estos son a menudo anónimos y aunque no soy fan (hubiera preferido un Lambda en muchos casos), vivo con ellos, pero no me gustan.

No he hecho ninguna C# en años, pero me pregunto si la prevalencia de las clases internas o cualquiera que sea el equivalente de C# se eliminen cuando introdujeron Lambdas.

+3

Para la referencia, debo mencionar que ** Java 8 ** presenta [** Lambda Expressions **] (https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html). – vellotis

+1

También, buena suerte unidad probando las clases internas ... – twiz

16

Limpieza. Es más fácil comprender el código si está dividido en partes lógicas, no todas formadas en el mismo archivo.

Dicho esto, no considero que el uso juicioso de las clases internas sea inapropiado. A veces, estas clases internas solo existen para un propósito, entonces no tendría ningún problema con que estén en el único archivo en el que se usan. Sin embargo, esto no ocurre tanto en mi experiencia.

+2

En mi experiencia, tan pronto como haces una clase interna, la necesitas en otro lugar de todos modos :) – Dolph

+12

Es más fácil de entender el código si está dividido en partes lógicas, sí, pero en * mi * libro, eso significa Lo mejor es definir algo lo más cercano posible al lugar donde se usa, en lugar de dividirlo arbitrariamente en un archivo propio. ;) – Porculus

+2

Las clases internas no le impiden usarlas fuera de la clase en la que viven.Los enumeraciones son un buen ejemplo, los Enume generalmente se usan para una sola clase, la convierten en una estática pública y luego se tiene un contexto de dónde se espera que se use Enum. Los patrones de fábrica son un buen lugar para que las clases internas produzcan las implementaciones sin contaminar el espacio de nombres del paquete con cosas que solo deberían ser instanciadas en ciertos casos controlados por el objeto Factory. –

6

Las clases anónimas son buenas para usar al hacer una programación basada en eventos, especialmente en swing.

+1

Por qué en la programación basada en eventos y no para otros usos, como las estructuras utilizadas para reagrupar numerosos parámetros, ... Esto es más un dogma que una explicación: -1. – Guillaume

1

Las clases internas son apropiadas cuando se intenta emular herencia múltiple. Es similar a lo que sucede bajo el capó con C++: cuando tienes herencia múltiple en C++, el diseño del objeto en la memoria es en realidad una concatenación de varias instancias de objetos; el compilador luego determina cómo se ajustará el puntero "this" cuando se invoca un método. En Java, no hay herencia múltiple, pero se puede usar una clase interna para proporcionar una "vista" de una instancia dada bajo otro tipo.

La mayoría de las veces, es posible mantener la herencia simple, pero ocasionalmente la herencia múltiple es la herramienta correcta para usar, y este es el momento de usar una clase interna.

Esto significa que las clases internas son de alguna manera más complejas que las clases habituales, de la misma manera que la herencia múltiple es más compleja que la herencia individual: muchos programadores tienen problemas para concentrarse en ese concepto. De ahí la "mejor práctica": evitar las clases internas porque confunde a tus compañeros de trabajo. En mi opinión, este no es un buen argumento, y en mi lugar de trabajo nos complace usar clases internas cuando lo consideramos apropiado.

(Un pequeño inconveniente de las clases internas es que añaden un nivel adicional de sangría en el código fuente. Este es un irritante poco a veces, cuando uno quiere mantener el código a menos de 79 columnas.)

1

Las clases internas anónimas se utilizan a menudo cuando tenemos que implementar la interfaz con un método, como Runnable, ActionListener y algunos otros.

Otro gran dispositivo de las clases internas anónimas es cuando no desea crear una subclase de alguna clase, pero debe anular uno (o dos) de sus métodos.

Las clases internas con nombre se pueden usar cuando se desea lograr coherencia entre dos clases. No son tan útiles como clases internas anónimas y no puedo estar seguro de que sea una buena práctica usarlas alguna vez.

Java también tiene clases anidadas (o estáticas internas). Se pueden usar cuando desea proporcionar algún acceso especial y los niveles de acceso estándar o públicos estándar no son suficientes.

0

sí, es bueno usarlos, cuando intenta mantener una clase cohesiva, y las clases nunca deben crearse instancias desde fuera de su contexto de la clase exterior, haga que los constructores sean privados y tenga una encapsulación cohesiva realmente agradable. Cualquiera que diga que debe NUNCA utilizarlos no sabe de lo que están hablando. Para los manejadores de eventos y otras cosas en las que sobresalen las clases internas anónimas, son mucho mejores que la alternativa de llenar el espacio de nombres de tu paquete con muchos manejadores de eventos que solo se aplican a una clase específica.

1

Las clases internas a menudo se usan para "pasar un comportamiento" como un parámetro de un método. Esta capacidad es soportada de una manera elegante por otros lenguajes con cierres. El uso de clases internas produce un código no elegante (en mi humilde opinión) debido a una limitación de idioma, pero es útil y ampliamente utilizado para manejar eventos y blocks en general con clases internas.

Así que yo diría que las clases internas son muy útiles.

3

Las clases internas anónimas tienen la ventaja de poder ver los campos y las variables en torno a la "nueva" instrucción. Esto puede dar lugar a un diseño muy limpio y es un enfoque bastante agradable (pero un poco prolijo) para "cómo podemos hacer una versión simple de las declaraciones lambda".

Las clases internas con nombre tienen la ventaja de tener un nombre, con suerte revelador, que puede documentarse de la manera habitual, pero que está vinculado a la clase circundante. Un ejemplo muy agradable es el patrón Builder, donde la clase interna es responsable de proporcionar el estado para el proceso de inicialización en lugar de tener numerosos constructores. Tales constructores no se pueden reutilizar entre clases, por lo que tiene mucho sentido que el Generador se vincule estrechamente con la clase principal.

4

Sí, prohibir las clases internas es una práctica útil, ya que encontrar un lugar donde se les prohíbe es una buena forma de advertirme de que no trabaje allí, y por lo tanto, de preservar mi cordura futura. :)

Como señala gicappa, las clases internas anónimas son las más cercanas que tiene Java a los cierres, y son extremadamente apropiadas para su uso en situaciones en las que el comportamiento de paso en un método es adecuado, al menos.

+1

Al menos esto me hace sonreír, ¡y ese es un buen uso de las reglas a veces estúpidas encontradas en las políticas de codificación de la compañía! – Guillaume

+0

Nada específico para los manejadores de eventos, la intención principal de las clases anidadas es la encapsulación. – bharatj

2

Ciertos marcos, como Wicket, realmente requieren clases internas anónimas.

Decir nunca es tonto. ¡Nunca digas nunca!Un ejemplo de buen uso podría ser una situación en la que tiene algún código heredado escrito por alguien donde muchas clases operan directamente en un campo Colección, y por cualquier razón, no puede cambiar esas otras clases, pero necesita condicionalmente duplicar operaciones a otro Colección. Lo más fácil es agregar este comportamiento a través de una clase interna anónima.

bagOfStuff = new HashSet(){ 
    @Override 
    public boolean add(Object o) { 
    boolean returnValue = super.add(o); 
    if(returnValue && o instanceof Job) 
    { 
     Job job = ((Job)o); 
     if(job.fooBar()) 
     otherBagOfStuff.add(job); 
    } 
    return returnValue; 
    } 
} 

Dicho esto, que sin duda se puede utilizar como cierres de un hombre pobre.

2

Como dijeron algunos otros, muchas veces, cuando se utiliza una clase interna anónima, que también se utiliza en algunos otros lugares también ...

De este modo se puede duplicar fácilmente el código de la clase interna a muchos lugares ... Esto no parece un problema cuando está utilizando clases internas muy simples para filtrar/clasificar colecciones, usando predicados, comparador o algo así ...

Pero debe saber que cuando usa 3 veces una clase interna anónima que lo hace exactamente lo mismo (por ejemplo, eliminar el "" de una Colección), en realidad estás creando 3 nuevas clases en el PermGen de Java.

Así que si todo el mundo utilizan las clases internas en todas partes, esto puede conducir a una aplicación que tiene un PermGen más grande. De acuerdo con la solicitud que esto puede ser un problema ... Si usted está trabajando en la industria, es posible programar aplicaciones integradas que tienen una memoria limitada, que se deben optimizar ...

Nota: este es también el motivo por el doble rizado sintaxis de refuerzo (innerclass anónima con el bloque de inicialización no estática) a veces se considera como un anti patrón:

new ArrayList<String>() {{ 
    add("java"); 
    add("jsp"); 
    add("servlets"); 
    }} 

usted debe preguntar a las personas que prohíbe que los use ... en mi humilde opinión, todo depende del contexto .. .

+0

este es un buen punto en contra de la clase interna. Al menos cuando se declara un comportamiento "estándar". – Guillaume

+0

Este es un uso real y tonto de las clases internas. ¿Por qué no simplemente crear la lista de matriz y agregar las cadenas? Mostrar un ejemplo tonto de clases internas no hace que todo el uso de ellos sea una mala práctica. – ncmathsadist

+0

@ncmathsadist no digo que sea agradable, pero a algunas personas les resulta más legible y lo usan en pruebas unitarias donde el permgen no es tan importante –

3

sugiero ser cauteloso cuando se utiliza si se necesita un parámetro de método. Acabo de encontrar una pérdida de memoria relacionada con eso. Implica HttpServlet usando GrizzlyContinuation.
En resumen aquí es el código erróneo:

public void doGet(HttpServletRequest request, final HttpServletResponse response){ 
    createSubscription(..., new SubscriptionListener(){ 
    public void subscriptionCreated(final CallController controller) { 
     response.setStatus(200); 
     ... 
     controller.resume(); 
    } 

    public void subscriptionFailed(){ 
     ... 
    } 

    public void subscriptionTimeout(){ 
     ... 
    }}); 
} 

Así que ya que el oyente se mantiene mediante la suscripción del HttpServletResponse también se mantiene en caso de que el oyente lo necesita (no obvio). Luego, la instancia de HttpServletResponse se lanzará solo si se elimina la suscripción. Si usa una clase interna que obtiene la respuesta en el constructor, puede establecerse como nula una vez que la llamada se reanudó liberando la memoria.

Úsalos pero tenga cuidado!

Martin

2

Un elemento que es no se menciona aquí es que un (no estático) clase interna lleva una referencia a él es clase que lo contiene. Más importante aún, la clase interna tiene acceso a miembros privados de su clase adjunta. Podría, potencialmente, romper la encapsulación.

no utilice un interior de primera clase si usted tiene una opción.

+0

¿Clase interna estática? Do not Si está subordinado a otra clase, conviértalo en una clase no pública en el mismo archivo. No utilizar las clases internas para los oyentes significa mantener cadenas dolorosas de captadores y variables de estado que se relacionan entre sí y que son simplemente un hash feo. – ncmathsadist

Cuestiones relacionadas