Esta pregunta es sobre si hacer que una clase anidada en Java sea una clase anidada estática o una clase anidada interna. Busqué por aquí y en Stack Overflow, pero realmente no pude encontrar ninguna pregunta sobre las implicaciones de diseño de esta decisión.
Las preguntas que encontré son sobre la diferencia entre clases anidadas estáticas e internas, lo cual es claro para mí. Sin embargo, todavía no he encontrado una razón convincente para usar una clase anidada estática en Java, con la excepción de las clases anónimas, que no considero para esta pregunta.
Aquí entiendo el efecto del uso de clases anidadas estáticas:
- Menos acoplamiento: generalmente obtenemos menos acoplamiento, ya que la clase no puede acceder directamente a los atributos de su clase externa. Menos acoplamiento generalmente significa mejor calidad de código, pruebas más fáciles, refactorización, etc.
- Individual
Class
: el cargador de clases no necesita ocuparse de una nueva clase cada vez, creamos un objeto de la clase externa. Simplemente obtenemos nuevos objetos para la misma clase una y otra vez.
Para una clase interna, generalmente encuentro que las personas consideran el acceso a los atributos de la clase externa como un profesional. Ruego diferir en este aspecto desde el punto de vista del diseño, ya que este acceso directo significa que tenemos un alto acoplamiento y si alguna vez queremos extraer la clase anidada en su clase separada de nivel superior, solo podemos hacerlo después de esencialmente girar en una clase anidada estática.
Entonces, mi pregunta se reduce a esto: ¿Me equivoco al suponer que el acceso al atributo disponible para las clases internas no estáticas conduce a un alto acoplamiento, por lo tanto, a una menor calidad del código, y que deduzco de esto que las clases anidadas (no anónimas) deberían generalmente ser estático?
O en otras palabras: ¿hay alguna razón convincente por la que uno preferiría una clase interna anidada?
fuente
Respuestas:
Joshua Bloch en el ítem 22 de su libro "Effective Java Second Edition" le dice cuándo usar qué clase de clase anidada y por qué. Hay algunas citas a continuación:
Un uso común de una clase miembro estática es como una clase auxiliar pública, útil solo en combinación con su clase externa. Por ejemplo, considere una enumeración que describe las operaciones admitidas por una calculadora. La enumeración de la operación debe ser una clase miembro estática pública de la
Calculator
clase Los clientes deCalculator
podrían referirse a operaciones usando nombres comoCalculator.Operation.PLUS
yCalculator.Operation.MINUS
.Un uso común de una clase de miembro no estático es definir un Adaptador que permita que una instancia de la clase externa se vea como una instancia de una clase no relacionada. Por ejemplo, las implementaciones de la
Map
interfaz suelen utilizar clases de miembros no estáticos para implementar sus vistas de recogida , que son devueltos porMap
'skeySet
,entrySet
yvalues
métodos. Del mismo modo, las implementaciones de las interfaces de recopilación, comoSet
yList
, generalmente usan clases de miembros no estáticos para implementar sus iteradores:Si declara una clase miembro que no requiere acceso a una instancia adjunta, coloque siempre el
static
modificador en su declaración, convirtiéndola en una clase miembro estática en lugar de no estática.fuente
MySet
instancias de clase externa . Podría imaginar que algo como un tipo dependiente de la ruta sea una diferencia, es decirmyOneSet.iterator.getClass() != myOtherSet.iterator.getClass()
, pero de nuevo, en Java esto en realidad no es posible, ya que la clase sería la misma para cada uno.Tiene razón al suponer que el acceso al atributo disponible para las clases internas no estáticas conduce a un alto acoplamiento, por lo tanto, a una menor calidad del código, y las clases internas (no anónimas y no locales ) generalmente deberían ser estáticas.
Las implicaciones de diseño de la decisión de hacer que la clase interna no sea estática se presentan en Java Puzzlers , Puzzle 90 (la fuente en negrita en la cita a continuación es mía):
Si está interesado, se proporciona un análisis más detallado de Puzzle 90 en esta respuesta en Stack Overflow .
Vale la pena señalar que lo anterior es esencialmente una versión extendida de la guía dada en el tutorial de Java Classes and Objects :
Por lo tanto, la respuesta a la pregunta que hizo en otras palabras por tutorial es que la única razón convincente para usar no estática es cuando se requiere acceso a los campos y métodos no públicos de una instancia adjunta .
La redacción del tutorial es algo amplia (esta puede ser la razón por la cual los Java Puzzlers intentan fortalecerla y reducirla). En particular, el acceso directo a los campos de instancia de cierre nunca ha sido realmente requerido en mi experiencia, en el sentido de que formas alternativas como pasarlas como parámetros de constructor / método siempre resultaron más fáciles de depurar y mantener.
En general, mis encuentros (bastante dolorosos) con la depuración de las clases internas que acceden directamente a los campos de la instancia de cierre causaron una fuerte impresión de que esta práctica se asemeja al uso del estado global, junto con los males conocidos asociados con él.
Por supuesto, Java hace que el daño de un "cuasi global" esté contenido dentro de la clase adjunta, pero cuando tuve que depurar una clase interna particular, sentí que una curita no ayudaba a reducir el dolor: todavía tenía tener en cuenta la semántica "extranjera" y los detalles en lugar de centrarse completamente en el análisis de un objeto problemático en particular.
En aras de la exhaustividad, puede haber casos en los que no se aplique el razonamiento anterior. Por ejemplo, según mi lectura de map.keySet javadocs , esta característica sugiere un acoplamiento estrecho y, como resultado, invalida los argumentos contra las clases no estáticas:
No es que lo anterior haga que el código involucrado sea más fácil de mantener, probar y depurar, pero al menos podría permitir a uno argumentar que las complicaciones coinciden / están justificadas por la funcionalidad prevista.
fuente
Algunas ideas falsas:
IIRC - En realidad, puede. (Por supuesto, si son campos de objetos, no tendrá un objeto externo para mirar. Sin embargo, si obtiene dicho objeto de cualquier parte, entonces puede acceder directamente a esos campos).
No estoy muy seguro de lo que quieres decir aquí. El cargador de clases solo está involucrado dos veces en total, una vez para cada clase (cuando se carga la clase).
La divagación sigue:
En Javaland parece que tenemos un poco de miedo para crear clases. Creo que tiene que sentirse como un concepto significativo. Generalmente pertenece a su propio archivo. Se necesita una placa repetitiva significativa. Necesita atención a su diseño.
Frente a otros lenguajes, por ejemplo, Scala, o tal vez C ++: no hay absolutamente ningún drama crear un pequeño titular (clase, estructura, lo que sea) en cualquier momento que dos bits de datos se unan. Las reglas de acceso flexible lo ayudan a reducir el límite, lo que le permite mantener el código relacionado físicamente más cerca. Esto reduce su "distancia mental" entre las dos clases.
Podemos alejar las preocupaciones de diseño sobre el acceso directo, manteniendo la clase interna privada.
(... Si hubiera preguntado '¿por qué una clase interna pública no estática ?', Esa es una muy buena pregunta, no creo que haya encontrado un caso justificado para ellos).
Puede usarlos en cualquier momento que ayude con la legibilidad del código y evite violaciones DRY y repetitivo. No tengo un ejemplo listo porque no sucede con tanta frecuencia en mi experiencia, pero sucede.
Las clases internas estáticas siguen siendo un buen enfoque predeterminado , por cierto. No estático tiene un campo oculto adicional generado a través del cual la clase interna se refiere a la externa.
fuente
Se puede usar para crear un patrón de fábrica. A menudo se usa un patrón de fábrica cuando los objetos creados necesitan parámetros de constructor adicionales para funcionar, pero son tediosos para proporcionar cada vez:
Considerar:
Usado como:
Lo mismo podría lograrse con una clase interna no estática:
Usar como:
Las fábricas se benefician de tener métodos nombrados específicamente, como
create
,createFromSQL
ocreateFromTemplate
. Aquí también se puede hacer lo mismo en forma de múltiples clases internas:Se necesita menos paso de parámetros de fábrica a clase construida, pero la legibilidad puede sufrir un poco.
Comparar:
vs
fuente