¿Cuál es el sentido de la “clase final” en Java?

Estoy leyendo un libro sobre Java y dice que puedes declarar que toda la clase es final . No puedo pensar en nada donde usaría esto.

Soy nuevo en la progtwigción y me pregunto si los progtwigdores realmente usan esto en sus progtwigs . Si lo hacen, ¿cuándo lo usan para que yo pueda entenderlo mejor y saber cuándo usarlo?

Si Java está orientado a objetos, y usted declara una clase final , ¿no detiene la idea de que la clase tenga las características de los objetos?

Si lo hacen, ¿cuándo lo usan para que yo pueda entenderlo mejor y saber cuándo usarlo?

Una clase final es simplemente una clase que no se puede extender .

(Esto no significa que todas las referencias a los objetos de la clase actuarían como si se hubieran declarado como final ).

Cuando es útil declarar una clase como final se trata en las respuestas a esta pregunta:

  • ¿Buenas razones para prohibir la herencia en Java?

Si Java está orientado a objetos, y usted declara una clase final , ¿no detiene la idea de que la clase tenga las características de los objetos?

En cierto sentido, sí.

Al marcar una clase como final, se desactiva una característica poderosa y flexible del idioma para esa parte del código. Sin embargo, algunas clases no deberían (y en algunos casos no pueden ) estar diseñadas para tener en cuenta las subclases de una buena manera. En estos casos, tiene sentido marcar la clase como definitiva, a pesar de que limita el OOP. (Sin embargo, recuerde que una clase final aún puede extender otra clase no final).


Artículo relacionado: Java: cuándo crear una clase final

¡En Java, los elementos con el modificador final no se pueden cambiar!

Esto incluye las clases finales, las variables finales y los métodos finales:

  • Una clase final no puede ser extendida por ninguna otra clase
  • Una variable final no puede reasignarse a otro valor
  • Un método final no puede ser anulado

Un escenario donde final es importante, cuando desea evitar la herencia de una clase, por razones de seguridad. Esto le permite asegurarse de que el código que está ejecutando no pueda ser anulado por alguien.

Otro escenario es para la optimización: me parece recordar que el comstackdor de Java enumera algunas llamadas a funciones de las clases finales. Entonces, si llama a ax() y a se declara final , sabemos en tiempo de comstackción qué será el código y podemos alinearlo con la función de llamada. No tengo idea de si esto está realmente hecho, pero con el final es una posibilidad.

El mejor ejemplo es

clase final pública Cadena

que es una clase inmutable y no puede ser extendida. Por supuesto, hay algo más que hacer que la clase final sea inmutable.

Si imagina la jerarquía de clases como un árbol (como lo es en Java), las clases abstractas solo pueden ser twigs y las clases finales son aquellas que solo pueden ser hojas. Las clases que caen en ninguna de esas categorías pueden ser tanto twigs como hojas.

No hay ninguna violación de los principios OO aquí, final es simplemente proporcionar una buena simetría.

En la práctica, desea utilizar el elemento final si desea que sus objetos sean inmutables o si está escribiendo una API, para indicar a los usuarios de la API que la clase no está destinada a la extensión.

Lectura pertinente: El principio abierto cerrado de Bob Martin.

Cita clave:

Las entidades de software (clases, módulos, funciones, etc.) deben estar abiertas para la extensión, pero cerradas para la modificación.

La palabra clave final es el medio para aplicar esto en Java, ya sea que se use en métodos o en clases.

La palabra clave final sí misma significa que algo es definitivo y no debe modificarse de ninguna manera. Si una clase se marca como final , no se puede extender o subclasificar. Pero la pregunta es ¿por qué marcamos una final clase? OMI hay varias razones:

  1. Normalización: algunas clases realizan funciones estándar y no están destinadas a ser modificadas, por ejemplo, clases que realizan diversas funciones relacionadas con manipulaciones de cadenas o funciones matemáticas, etc.
  2. Razones de seguridad : a veces escribimos clases que realizan diversas funciones relacionadas con la autenticación y la contraseña, y no queremos que otras personas las modifiquen.

He oído que la finalización de la clase de marcado mejora la eficiencia, pero francamente no pude encontrar este argumento para tener mucho peso.

Si Java está orientado a objetos, y usted declara una clase final, ¿no detiene la idea de que la clase tenga las características de los objetos?

Quizás sí, pero a veces ese es el propósito. A veces hacemos eso para lograr mayores beneficios de seguridad, etc. al sacrificar la capacidad de esta clase para extenderse. Pero una clase final aún puede extender una clase si es necesario.

En una nota lateral, deberíamos preferir la composición a la herencia y final palabra clave final realmente ayuda a hacer cumplir este principio.

Si la clase se marca como final , significa que la estructura de la clase no puede ser modificada por algo externo. Donde este es el más visible es cuando estás haciendo herencia polimórfica tradicional, básicamente la class B extends A simplemente no funcionará. Básicamente es una forma de proteger algunas partes de tu código (hasta cierto punto) .

Para aclarar, marcar la clase final no marca sus campos como final y, como tal, no protege las propiedades del objeto, sino la estructura de clase real en su lugar.

Tenga cuidado cuando haga una clase “final”. Porque si desea escribir una prueba unitaria para una clase final, no puede subclasificar esta clase final para utilizar la técnica de división de dependencias “Método de subclase y modificación” descrita en el libro de Michael C. Feathers “Trabajar eficazmente con código heredado” . En este libro, Feathers dijo: “En serio, es fácil creer que el sello y el final son un error de cabeza equivocada, que nunca deberían haber sido añadidos a los lenguajes de progtwigción. Pero la verdadera falla recae en nosotros. Cuando dependemos directamente de bibliotecas que están fuera de nuestro control, solo estamos buscando problemas “.

PARA ABORDAR EL PROBLEMA DE LA CLASE FINAL:

Hay dos formas de hacer una clase final. El primero es usar la palabra clave final en la statement de clase:

 public final class SomeClass { // . . . Class contents } 

La segunda forma de hacer una clase final es declarar todos sus constructores como privados:

 public class SomeClass { public final static SOME_INSTANCE = new SomeClass(5); private SomeClass(final int value) { } 

Marcarlo final le ahorra la molestia si descubre que es real una final, para demostrar que mira esta clase de prueba. se ve público a primera vista.

 public class Test{ private Test(Class beanClass, Class stopClass, int flags) throws Exception{ // . . . snip . . . } } 

Desafortunadamente, dado que el único constructor de la clase es privado, es imposible extender esta clase. En el caso de la clase de prueba, no hay ninguna razón para que la clase sea final. La clase de prueba es un buen ejemplo de cómo las clases finales implícitas pueden causar problemas.

Por lo tanto, debe marcarlo como definitivo cuando implícitamente haga una clase final haciendo que su constructor sea privado.

final class puede evitar romper la API pública cuando agrega nuevos métodos

Supongamos que en la versión 1 de su clase Base hace:

 public class Base {} 

y un cliente hace:

 class Derived extends Base { public int method() { return 1; } } 

Entonces, si en la versión 2 quieres agregar un method método a Base :

 class Base { public String method() { return null; } } 

Rompería el código del cliente.

Si hubiésemos usado final class Base cambio, el cliente no habría podido heredar, y la adición del método no rompería la API.

Sí, a veces puede desear esto, ya sea por razones de seguridad o velocidad. Se hace también en C ++. Puede que no sea aplicable a los progtwigs, sino a los marcos. http://www.glenmccl.com/perfj_025.htm

Una clase final es una clase que no se puede extender. También los métodos podrían declararse como finales para indicar que las subclases no pueden anularlos.

Evitar que la clase sea subclasificada podría ser particularmente útil si escribe API o bibliotecas y desea evitar que se amplíe para alterar el comportamiento de la base.

Una ventaja de mantener una clase como final: –

La clase de cadena se mantiene final para que nadie pueda anular sus métodos y cambiar la funcionalidad. por ejemplo, nadie puede cambiar la funcionalidad del método length (). Siempre devolverá la longitud de una cadena.

El desarrollador de esta clase no quería que nadie cambiara la funcionalidad de esta clase, por lo que la mantuvo como definitiva.

Las clases finales no se pueden extender. Por lo tanto, si desea que una clase se comporte de cierta manera y no haga que alguien anule los métodos (con un código posiblemente menos eficiente y más malicioso), puede declarar toda la clase como métodos finales o específicos que no desea que sean. cambiado

Como declarar una clase no evita que una clase sea instanciada, no significa que impedirá que la clase tenga las características de un objeto. Es solo que tendrá que apegarse a los métodos tal como están declarados en la clase.

pensar en FINAL como el “Fin de la línea” – ese tipo ya no puede producir descendencia. Entonces, cuando lo veas de esta manera, hay un montón de escenarios del mundo real con los que te cruzarás y que requieren que marques un marcador de ‘fin de línea’ para la clase. Es un diseño impulsado por el dominio: si su dominio exige que una determinada ENTIDAD (clase) no pueda crear subclases, márquelo como FINAL.

Debo señalar que no hay nada que te impida heredar una clase “debería etiquetarse como final”. Pero eso generalmente se clasifica como “abuso de la herencia”, y se hace porque la mayoría de las veces le gustaría heredar alguna función de la clase base de su clase.

El mejor enfoque es mirar el dominio y dejar que dicte sus decisiones de diseño.

Como se dijo anteriormente, si desea que nadie pueda cambiar la funcionalidad del método, puede declararlo como definitivo.

Ejemplo: ruta del archivo del servidor de aplicaciones para descarga / carga, división de la cadena en función del desplazamiento, dichos métodos pueden declararse finales para que no se modifiquen estas funciones del método. Y si desea esos métodos finales en una clase separada, defina esa clase como clase Final. Entonces, la clase Final tendrá todos los métodos finales, mientras que el método Final se puede declarar y definir en una clase no final.

La clase Android Looper es un buen ejemplo práctico de esto. http://developer.android.com/reference/android/os/Looper.html

La clase Looper proporciona cierta funcionalidad que NO debe ser reemplazada por ninguna otra clase. Por lo tanto, no hay subclase aquí.

La clase final se usa cuando desea estandarizar los métodos y asegurarse de que no sean sobrescritos por otra persona.
Entonces, ¿por qué algunas clases se declaran como Final?

  1. Dado que las clases finales no se pueden extender, puede tratarlas como primitivas y no tiene que preocuparse de que se cambien estas clases.
  2. Por razones de seguridad, no desea que el código escrito por usted, sea anulado por otra persona.

Si haces una clase como final, no puedes extenderla.

 final class Bike{} class Honda1 extends Bike{ void run(){System.out.println("running safely with 100kmph");} public static void main(String args[]){ Honda1 honda= new Honda(); honda.run(); } } Output:Compile Time Error 

En breve, una clase, variable o método declarado como final no se puede cambiar.

Lo que es más importante es mi opinión:

Honestamente, creo que una palabra clave final es un error porque su existencia le permite a un usuario definir algo que no es final . Todo en Java debe ser final por defecto, excepto los métodos de interfaz (según su definición). Fue una mala elección permitir que todos los métodos de instancia sean virtual por defecto (en términos de c # ). La clave es forzar al usuario a pensar primero y explícitamente definir vergonzosamente un método como virtual lugar: le haría preguntarse si este método debería permitir que otros lo anulen, si really una necesidad o lo persuadan a rediseñar su código.

Supongamos que tiene una clase Employee que tiene un método de greet . Cuando se llama al método de greet , simplemente imprime Hello everyone! . Entonces ese es el comportamiento esperado del método greet

 public class Employee { void greet() { System.out.println("Hello everyone!"); } } 

Ahora, deje que GrumpyEmployee subclass Employee y anule el método de greet como se muestra a continuación.

 public class GrumpyEmployee extends Employee { @Override void greet() { System.out.println("Get lost!"); } } 

Ahora en el siguiente código, eche un vistazo al método sayHello . Toma la instancia de Employee como parámetro y llama al método de bienvenida con la esperanza de que diga Hello everyone! ¡Pero lo que conseguimos es Get lost! . Este cambio en el comportamiento se debe a Employee grumpyEmployee = new GrumpyEmployee();

 public class TestFinal { static Employee grumpyEmployee = new GrumpyEmployee(); public static void main(String[] args) { TestFinal testFinal = new TestFinal(); testFinal.sayHello(grumpyEmployee); } private void sayHello(Employee employee) { employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!" } } 

Esta situación se puede evitar si la clase Employee se hizo final . Imagínense la cantidad de caos que un progtwigdor descarado podría causar si String Clase String no se declarase como final .

La orientación a objetos no se trata de herencia, se trata de encapsulación. Y la herencia rompe la encapsulación.

Declarar una final de clase tiene mucho sentido en muchos casos. Cualquier objeto que represente un “valor” como un color o una cantidad de dinero podría ser final. Ellos se sostienen solos.

Si está escribiendo bibliotecas, haga que sus clases sean definitivas, a menos que las sangre explícitamente para derivarlas. De lo contrario, las personas pueden derivar sus clases y anular métodos, rompiendo sus suposiciones / invariantes. Esto puede tener implicaciones de seguridad también.

Joshua Bloch en “Effective Java” recomienda diseñar explícitamente para la herencia o prohibirlo y señala que diseñar para la herencia no es tan fácil.