¿Cuál es el punto de "clase final" en Java?

569

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

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

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

novato
fuente

Respuestas:

531

En primer lugar, recomiendo este artículo: Java: cuándo crear una clase final


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

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

(No significa que todas las referencias a objetos de la clase actuarían como si fueran declaradas como final).

Cuando es útil declarar una clase como final está cubierto en las respuestas de esta pregunta:

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

En cierto sentido si.

Al marcar una clase como final, deshabilita una característica potente y flexible del idioma para esa parte del código. Sin embargo, algunas clases no deberían (y en ciertos casos no pueden ) diseñarse para tener en cuenta las subclases de una buena manera. En estos casos, tiene sentido marcar la clase como final, a pesar de que limita la POO. (Recuerde, sin embargo, que una clase final todavía puede extender otra clase no final).

aioobe
fuente
39
Para agregar a la respuesta, uno de los principios de Java efectivo es favorecer la composición sobre la herencia. El uso de la palabra clave final también ayuda a hacer cumplir ese principio.
Riggy
99
"Lo haces principalmente por razones de eficiencia y seguridad". Escucho este comentario con bastante frecuencia (incluso Wikipedia lo dice) pero todavía no entiendo el razonamiento detrás de este argumento. ¿Alguien quiere explicar cómo, por ejemplo, un java.lang.String no final habría resultado ineficiente o inseguro?
MRA
27
@MRA Si creo un método que acepta una cadena como parámetro, supongo que es inmutable, porque las cadenas son. Como resultado de esto, sé que puedo llamar a cualquier método en el objeto String de manera segura, y no cambiar el String pasado. Si tuviera que extender String y cambiar la implementación de subcadena para cambiar la cadena real, entonces el objeto String que esperaba que fuera inmutable ya no es inmutable.
Cruncher
1
@Sortofabeginner Y tan pronto como diga que desea que todos los métodos y campos de Cadena sean finales, solo para que pueda crear alguna clase con funcionalidad adicional ... En ese momento, también podría crear una clase que tenga una cadena y crear métodos que operen en esa cadena.
Cruncher
1
@Shay final (entre otras cosas) se usa para hacer que un objeto sea inmutable, por lo que no diría que no tienen nada que ver entre sí. Ver aquí docs.oracle.com/javase/tutorial/essential/concurrency/…
Celeritas
184

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

Esto incluye clases finales, variables finales y 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
andyqee
fuente
40
La pregunta real es por qué , no qué .
Francesco Menzani
8
La declaración, "¡En Java, los elementos con el finalmodificador no se pueden cambiar!", Es demasiado categórica y, de hecho, no del todo correcta. Como dijo Grady Booch, "Un objeto tiene estado, comportamiento e identidad". Si bien no podemos cambiar la identidad de un objeto una vez que su referencia se ha marcado como final, sí tenemos la oportunidad de cambiar su estado mediante la asignación de nuevos valores a sus no finalcampos (siempre que, por supuesto, los tenga). Cualquiera que sea planear obtener una Certificación Oracle Java (como 1Z0-808, etc.) debe tener esto en cuenta porque puede haber preguntas sobre este aspecto en el examen ...
Igor Soudakevitch
33

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

Otro escenario es para la optimización: parece recordar que el compilador de Java incluye algunas llamadas de función de las clases finales. Entonces, si llama a.x()y se declara a final, sabemos en tiempo de compilación cuál será el código y podemos incorporarlo a la función de llamada. No tengo idea de si esto se hace realmente, pero con final es una posibilidad.

Shay Rojansky
fuente
77
La alineación normalmente solo la realiza el compilador justo a tiempo en tiempo de ejecución. También funciona sin final, pero el compilador JIT tiene un poco más de trabajo que hacer para asegurarse de que no haya clases extendidas (o que estas clases extendidas no toquen este método).
Paŭlo Ebermann
Un buen artículo sobre el tema de la alineación y la optimización se puede encontrar aquí: lemire.me/blog/archives/2014/12/17/…
Josh Hemann
24

El mejor ejemplo es

Cadena de clase final pública

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

javadeveloper
fuente
Jeje, a veces protege a los desarrolladores de Rube Goldergian de ellos mismos.
Zoidberg
16

Lectura relevante: El principio abierto-cerrado de Bob Martin.

Cita clave:

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

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

Sean Patrick Floyd
fuente
66
@Sean: ¿Al declararlo, finalla clase no se cierra por extensión en lugar de abrirse? ¿O lo estoy tomando demasiado literalmente?
Goran Jovic
44
@Goran aplicando globalmente final, sí. La clave es aplicar selectivamente el final en lugares donde no desea modificaciones (y, por supuesto, proporcionar buenos ganchos para la extensión)
Sean Patrick Floyd
26
En OCP, "modificación" se refiere a la modificación del código fuente, y "extensión" se refiere a la herencia de implementación. Por lo tanto, el uso de finaluna declaración de clase / método no tendría sentido si desea que el código de implementación se cierre por modificación pero se abra por extensión por herencia.
Rogério
1
@Rogerio He tomado prestada la referencia (y la interpretación) de Spring Framework Reference (MVC) . En mi humilde opinión, esto tiene mucho más sentido que la versión original.
Sean Patrick Floyd
La extensión está muerta. Inútil. Diezmado. Destruido. No me importa el OCP. Nunca hay una excusa para extender una clase.
Josh Woodcock
15

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

Aquí no hay violación de los principios de OO, lo final es simplemente proporcionar una buena simetría.

En la práctica, desea usar 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 simplemente no está destinada a la extensión.

biziclop
fuente
13

La palabra clave en finalsí misma significa que algo es definitivo y no se debe modificar de ninguna manera. Si una clase está marcada, finalentonces no se puede extender o subclasificar. Pero la pregunta es ¿por qué marcamos una clase final? OMI hay varias razones:

  1. Estandarización: Algunas clases realizan funciones estándar y no están destinadas a ser modificadas, por ejemplo, clases que realizan varias funciones relacionadas con manipulaciones de cadenas o funciones matemáticas, etc.
  2. Razones de seguridad : a veces escribimos clases que realizan varias funciones relacionadas con la autenticación y la contraseña y no queremos que nadie más las modifique.

He oído que la clase de marcado finalmejora la eficiencia, pero francamente no pude encontrar que este argumento tenga mucho peso.

Si Java está orientado a objetos, y declaras 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 previsto. A veces lo hacemos 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 al margen, deberíamos preferir la composición sobre la herencia y la finalpalabra clave realmente ayuda a hacer cumplir este principio.

akhil_mittal
fuente
6

Tenga cuidado cuando haga una clase "final". Porque si desea escribir una prueba de unidad para una clase final, no puede subclasificar esta clase final para utilizar la técnica de ruptura de dependencia "Subclase y método de anulació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 sellado y final es un error equivocado, que nunca deberían haberse agregado a los lenguajes de programación. Pero la verdadera falla recae en nosotros. Cuando dependemos directamente de bibliotecas que están fuera de nuestro control, solo estamos pidiendo problemas ".

Ben Wu
fuente
6

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

Supongamos que en la versión 1 de tu Baseclase haces:

public class Base {}

y un cliente hace:

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

Entonces, si en la versión 2 desea agregar un methodmétodo para Base:

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

rompería el código del cliente.

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

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
5

Si la clase está marcada final, significa que la estructura de la clase no puede ser modificada por nada externo. Donde esto es más visible es cuando estás haciendo una herencia polimórfica tradicional, básicamente class B extends Ano funcionará. Básicamente es una forma de proteger algunas partes de su código (hasta cierto punto) .

Para aclarar, la clase de marcado finalno marca sus campos como finaly como tal no protege las propiedades del objeto, sino la estructura de clase real.

Esko
fuente
1
¿Qué significan las propiedades del objeto? ¿Significa que podría modificar la variable miembro de la clase si la clase se declara final? Entonces, el único propósito de la clase final es evitar la herencia.
Adam Lyu
5

PARA ABORDAR EL PROBLEMA DE CLASE FINAL:

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

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

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

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

Marcarlo como final te ahorra el problema si descubres que es una final real, para demostrar que miras esta clase de Prueba. parece 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 Test, no hay razón para que la clase sea final. La clase Test es un buen ejemplo de cómo las clases finales implícitas pueden causar problemas.

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

mel3kings
fuente
4

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

Evitar que la clase se subclasifique podría ser particularmente útil si escribe API o bibliotecas y desea evitar que se extienda para alterar el comportamiento base.

JuanZe
fuente
4

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 final.

Rahul
fuente
3

Sí, a veces es posible que desee esto, ya sea por razones de seguridad o velocidad. También se hace en C ++. Puede no ser tan aplicable para los programas, pero más aún para los marcos. http://www.glenmccl.com/perfj_025.htm

James
fuente
3

En java, la palabra clave final se usa para las siguientes ocasiones

  1. Variables finales
  2. Métodos finales
  3. Clases finales

En Java, las variables finales no se pueden reasignar, las clases finales no se pueden extender y los métodos finales no se pueden anular.

Kavinda Pushpitha
fuente
1

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

Dado que declarar una clase no impide que se instancia una clase, no significa que evitará que la clase tenga las características de un objeto. Es solo que tendrá que atenerse a los métodos tal como se declaran en la clase.

skhaapioloir
fuente
1

Piense en FINAL como el "Fin de la línea": ese tipo ya no puede producir descendencia. Entonces, cuando lo ve de esta manera, hay un montón de escenarios del mundo real que encontrará que requieren que marque un marcador de 'fin de línea' a la clase. Es un diseño impulsado por dominio: si su dominio exige que una ENTIDAD (clase) determinada no pueda crear subclases, márquela como FINAL.

Debo señalar que no hay nada que le impida heredar una clase "debe etiquetarse como final". Pero eso generalmente se clasifica como "abuso de 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.

Kingz
fuente
1

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

Ejemplo: ruta del archivo del servidor de aplicaciones para descarga / carga, división de cadena en función del desplazamiento, tales métodos puede declararlo Final para que estas funciones de método no se alteren. Y si desea tales métodos finales en una clase separada, defina esa clase como clase Final. Por lo tanto, la clase Final tendrá todos los métodos finales, donde el método Final se puede declarar y definir en una clase no final.

lok
fuente
1

Digamos que tienes una Employeeclase que tiene un método greet. Cuando greetse llama al método, simplemente se imprime Hello everyone!. Ese es el comportamiento esperado del greetmétodo

public class Employee {

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

Ahora, deje que la GrumpyEmployeesubclase Employeey anule el greetmétodo como se muestra abajo.

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

Ahora, en el siguiente código, eche un vistazo al sayHellométodo. Toma Employeeinstancia como parámetro y llama al método greet esperando que diga Hello everyone!Pero lo que obtenemos es Get lost!. Este cambio de comportamiento se debe aEmployee 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 Employeese hizo la clase final. Imagínense la cantidad de caos que un programador descarado podría causar si StringClass no se declarara como final.

Andy
fuente
1

La clase final no se puede extender más. Si no necesitamos hacer que una clase sea heredable en Java, podemos usar este enfoque.

Si solo necesitamos crear métodos particulares en una clase para no ser anulados, simplemente podemos poner la palabra clave final delante de ellos. Allí la clase todavía es heredable.

G. Kith
fuente
-1

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 definitivo. Se paran solos.

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

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

Wilhem Meignan
fuente