2008-10-20 15 views
132

¿Cuáles son buenas razones para prohibir la herencia en Java, por ejemplo, mediante el uso de clases finales o clases usando un solo constructor privado sin parámetros? ¿Cuáles son buenas razones para hacer que un método sea definitivo?¿Buenas razones para prohibir la herencia en Java?

+0

Pedantería: El constructor predeterminado es el generado por el compilador de Java si no escribe explícitamente uno en su código. Te refieres al constructor sin argumentos. –

+0

¿No es ese el "constructor implícito implícito"? – cretzel

+1

"No tiene que proporcionar ningún constructor para su clase, pero debe tener cuidado al hacerlo. El compilador proporciona automáticamente un constructor predeterminado sin argumento para cualquier clase sin constructores". http://java.sun.com/docs/books/tutorial/java/javaOO/constructors.html - John Topley –

Respuesta

137

Su mejor referencia aquí es el artículo 17 (15 en la primera edición) del excelente libro de Joshua Bloch "Effective Java", llamado "Diseño y documento para herencia o bien lo prohíbe". Realmente deberías leerlo, pero lo resumiré.

La interacción de clases heredadas con sus padres puede ser sorprendente e impredecible si el antepasado no fue diseñado para ser heredado. Por lo tanto,

Las clases deben ser de dos tipos:

  1. Clases diseñados para ser extendieron, y con suficiente documentación para describir la forma en que debe hacerse

  2. Clases marcados definitiva

Si está escribiendo código puramente interno esto puede ser un poco exagerado. Sin embargo, el esfuerzo adicional que implica agregar cinco caracteres a un archivo de clase es muy pequeño. Si está escribiendo solo para consumo interno, un codificador futuro siempre puede eliminar el 'final'; puede pensarlo como una advertencia que dice "esta clase no fue diseñada con herencia en mente".

+0

Esa es una buena respuesta, pero es un poco excesivo en la mayoría de las situaciones. – wprl

+4

En mi experiencia esto no es excesivo si alguien más que yo usará el código. Nunca entendí por qué Java tiene métodos anulables por defecto. –

+6

Para entender algo de Java efectivo, debes entender el enfoque de Josh en el diseño. Él dice [algo así como] siempre deberías diseñar interfaces de clase como si fueran una API pública. Creo que la opinión de la mayoría es que este enfoque suele ser demasiado pesado e inflexible. (YAGNI, etc.) –

20

Es posible que desee hacer un método final para que las clases superiores no puedan cambiar el comportamiento que se cuenta en otros métodos. Los métodos llamados en los constructores a menudo se declaran definitivos para que no tenga sorpresas desagradables al crear objetos.

+0

No entendí bien esta respuesta. Si no te importa, ¿podrías explicar a qué te refieres con que "anular las clases no puede cambiar el comportamiento que se cuenta en otros métodos"?En primer lugar, pregunto porque no entendí la oración, y en segundo lugar, porque Netbeans recomienda que una de mis funciones a las que llamo desde el constructor sea 'final '. – Nav

+1

@Nav Si realiza un método final, una clase superior no podrá tener su propia versión de ese método (o será un error de compilación). De esta forma, usted sabe que el comportamiento que implementa en ese método no cambiará más tarde cuando alguien más amplíe la clase. –

14

Una razón para hacer una clase final sería si desea forzar la composición sobre la herencia. Esto es generalmente deseable para evitar un acoplamiento estrecho entre clases.

6

La herencia es como una motosierra: muy potente, pero horrible en las manos equivocadas. O bien diseñas una clase para heredarla (lo que puede limitar la flexibilidad y tardas mucho más) o debes prohibirla.

Consulte los artículos 16 y 17 de Effective Java 2nd edition, o la publicación de mi blog "Inheritance Tax".

+0

El enlace a la publicación del blog está roto. Además, ¿crees que podrías elaborar un poco más sobre esto? –

+0

@MichaelT: Se corrigió el enlace, gracias - sígalo para más detalles :) (No tengo tiempo para agregarlo ahora, me temo) –

+0

@JonSkeet, el enlace a la entrada del blog está roto . – Kedarnath

1

Para evitar que las personas hagan cosas que podrían confundirse a ellos mismos y a los demás. Imagine una biblioteca de física donde tiene algunas constantes o cálculos definidos. Sin usar la palabra clave final, alguien podría venir y redefinir cálculos básicos o constantes que NUNCA deberían cambiar.

+2

Hay algo que no entiendo sobre ese argumento - si alguien cambió esos cálculos/constantes que no deberían cambiar - ¿no hay fallas debido a que el 100% es su culpa? En otras palabras, ¿cómo el cambio beneficia a algo? –

+0

Sí, técnicamente es "su" error, pero imagine que alguien más que esté utilizando la biblioteca que no se haya enterado de los cambios, podría generar confusión. Siempre pensé que esa era la razón por la que muchas de las clases de Java Base se marcaban como definitivas, para evitar que las personas alteren la funcionalidad básica. –

+0

@matt b - Parece que asume que el desarrollador que hizo el cambio lo hizo a sabiendas o que les importaría que fuera su culpa. Si alguien, además de mí, va a usar mi código, lo marcaré como definitivo, a menos que esté destinado a ser cambiado. –

4

Hmmm ... No puedo pensar en dos cosas:

Es posible que haya una clase que se ocupa de ciertos problemas de seguridad. Al crear una subclase y alimentar a su sistema con la versión subclase, un atacante puede eludir las restricciones de seguridad. P.ej. su aplicación podría admitir complementos y si un complemento puede simplemente subclasificar sus clases de seguridad relevantes, puede usar este truco para contrabandear una versión subclasificada de ella en su lugar. Sin embargo, esto es más bien algo con lo que Sun tiene que lidiar con respecto a los applets y cosas por el estilo, tal vez no sea un caso tan realista.

Una mucho más realista es evitar que un objeto se vuelva mutable. P.ej.Como las cadenas son inmutables, su código puede mantener con seguridad las referencias a ella

String blah = someOtherString; 

en lugar de copiar la primera cadena. Sin embargo, si puede subclase String, puede agregarle métodos que permitan modificar el valor de la cadena, ahora ningún código puede confiar más en que la cadena se mantendrá igual si solo copia la cadena como se indica anteriormente, en su lugar debe duplicar el cuerda.

1

Además, si está escribiendo una clase de fuente cerrada comercial, es posible que no desee que la gente pueda cambiar la funcionalidad más adelante, especialmente si necesita dar soporte y las personas han anulado su método y se están quejando que llamarlo da resultados inesperados.

1

Si marca las clases y los métodos como definitivos, puede observar una pequeña ganancia de rendimiento, ya que el tiempo de ejecución no tiene que buscar el método de clase correcto para invocar un objeto determinado. Los métodos no finales se marcan como virtuales para que puedan extenderse adecuadamente si es necesario, los métodos finales se pueden vincular directamente o compilar en línea en la clase.

+1

Creo que esto es un mito, ya que las máquinas virtuales de generación actual deberían poder optimizar y desoptimizar según sea necesario. Recuerdo haber visto esto y clasificarlo como un mito. –

+1

La diferencia en la generación de código no es un mito, pero tal vez las ganancias de rendimiento sí lo son. Como dije es una pequeña ganancia, un sitio mencionó una ganancia de rendimiento del 3.5%, algo más alto, que en la mayoría de los casos no vale la pena ir a todos los nazis en su código. – seanalltogether

+1

No es un mito. De hecho, el compilador solo puede alinear los métodos finales en V. No estoy seguro de si algún JIT está "en línea" en tiempo de ejecución artístico, pero dudo que pueda hacerlo, ya que puede estar cargando una clase derivada más tarde. – Uri

12

Hay 3 casos de uso donde puede ir para los métodos finales.

  1. Para evitar que la clase derivada invalide una funcionalidad particular de la clase base.
  2. Esto es por motivos de seguridad, donde la clase base proporciona una funcionalidad central importante del marco donde no se supone que la clase derivada la modifique.
  3. Los métodos finales son más rápidos que los métodos de instancia, ya que no se utiliza el concepto de tabla virtual para los métodos finales y privados. Entonces, donde sea que haya una posibilidad, intente utilizar los métodos finales.

Propósito para hacer una clase final:

Para que ningún cuerpo puede extender esas clases y cambiar su comportamiento.

Ejemplo: clase de envoltura Entero es una clase final. Si esa clase no es definitiva, cualquiera puede extender entero en su propia clase y cambiar el comportamiento básico de la clase entera. Para evitar esto, Java hizo todas las clases contenedoras como clases finales.

+0

No estoy muy convencido con su ejemplo. Si ese es el caso, ¿por qué no hacer que los métodos y variables sean definitivos en lugar de hacer que toda la clase sea definitiva? ¿Puedes por favor editar tu respuesta con los detalles? – Rajkiran

+3

Incluso si hace que todas las variables y métodos sean definitivos, otros pueden heredar su clase y agregar funcionalidades adicionales y cambiar el comportamiento básico de su clase. – user1923551

+0

¡Oh! Maldita sea Gracias. – Rajkiran

0

Quiere hacer que un método sea definitivo para que anular las clases no modifique su comportamiento. Cuando desee poder cambiar el comportamiento, haga público el método. Cuando anula un método público, puede cambiarse.

Cuestiones relacionadas