uso el término, «Problema inicio-fin» para describir un recurso que tiene que ser abierto y cerrado, o un encabezado y pie de página que debe ser impreso en una forma que tentará a hacer una clase con ()
métodos, que están destinados a ser llamado como un par con otro código en el medio. En algún momento alrededor de 1997, mi mentor Jack Follansbee me dijo que evitara este patrón siempre que sea práctico, porque es demasiado fácil olvidarse de llamar a la final ()
. Otro problema sería si el código en el medio realiza un retorno
, continuar
, romper
, se emite una excepción
, o de otra manera se evita llegar al final ()
.
Java de try-catch-finally
Bloque resuelve la devolución
y Excepción
problemas, pero no garantiza que se le recuerde llamar el final ()
en el finalmente
bloque. Hay tres tradicionales de Java se acerca a este problema:
- Pre-evaluación de el código de centro de alguna manera (por ejemplo, con una amortiguación) y pasarlo a un procedimiento que añade a la de inicio y finalización de la memoria intermedia.
public void doStartEnd (StringBuilder sB) {
sB.insert (0, "Inicio" );
sb.append ( "End" );
} - Inyección de Dependencia: La creación de un procedimiento de uso especial con las variables para su adaptación a múltiples fines relacionados. Si el número de variables es pequeña (o 0) esto funciona muy bien. Pero a veces múltiples variables tienen que ser pasado a este procedimiento y hasta una compleja estructura de datos se deben crear para contener los valores actualizados de el tipo de retorno. Me parece una pérdida de tiempo y energía para cargar todas las variables en los argumentos de procedimientos como los pasajeros de un autobús y luego descargado sus valores actualizados desde el retorno de tipo de datos de uno en uno.
public class ReturnObject {
public int contar;
public boolean showedAnything;
}
público ReturnObject doStartEnd ( int contar, medio de cuerdas, showedAnything) {
System.out.println ( "Inicio" ),
si {System.out.println ( "Cosas nuevas" );} (showedAnything!)
de (; neutrófilos <5; cuenta + +) {
showedAnything = true ,
System.out. println (centro);}
System.out.println ( "End" );
ReturnObject ro = new ReturnObject ();
ro.count = cuenta;
ro.showedAnything = showedAnything;
ro;
} - Crear una interfaz para el código de centro que se está ejecutando y tienen un objeto implementar esa interfaz. Un objeto bien diseñado, posiblemente con un patrón Builder puede mitigar algunos de los problemas de la estación de autobuses, pero ya que este patrón es más que el golpeteo de Inyección de Dependencia vuelto de dentro a fuera, a veces simplemente empuja la cabeza de inyección de dependencia de la solución anterior a partir de un procedimiento a un objeto. Esto solía ser la solución más general al problema de puesta en extremo. El primer ejemplo siguiente utiliza una clase abstracta y una implementación en el anonimato, pero eso es sólo una variación de menor importancia en esta técnica.
Un programador funcionales diría que un cierre léxico sería la solución obvia a este problema. Se permite que una cantidad arbitraria de código (incluyendo las variables locales de la persona que llama) para ser ejecutado en el medio de algún otro código. No pre-evaluación, no la estación de autobuses, no hay objetos o interfaces adicionales. El exterior «encerrar» el código puede hacer el start ()
y el final ()
lógica con el código «encerrado» en un pequeño sobre el cierre mágico en el centro. Java no tiene cierres o punteros a funciones, pero una clase interna anónima parece algo así como un cierre y el código se compila, obras, e incluso se resuelve el problema de inicio de extremo para algunos casos especiales, aunque tal vez no ganar muchos concursos de belleza :
public class ClosureTest {
clase abstracta estática privado STARTEND {
public void doStartEnd () {
System.out.println ( "Inicio" ),
medio ();
System.out.println ( "End" );}
abstract void público Oriente ();}
public void estática main (String [] args) {
nueva STARTEND () {
@ Override
Oriente () {
System.out.println ( "Medio" );}
} doStartEnd ().;
}}
salida:
Iniciar
Medio End
Una triste limitación de esta técnica es que Java trata de evitar que la actualización del principal ()
variables locales del método dentro de la clase interna anónima por lo que obligó a hacerlos definitiva
(inmutable). Lo siguiente no se compilará:
void static main (String [] args) {
int count = 0;
nueva STARTEND () {
@ Override
public void Oriente () {
/ / ERROR: conteo variable local se accede desde el interior
/ / interior clase, tiene que ser declarados definitivos
para (; neutrófilos <5; cuenta + +) {
System.out. println ( "Medio" );}
}} doStartEnd ();.
System.out.println ( "Count Total" + count);}
Una clase contenedora mutable trabajará en torno a esta restricción. Más feo, pero funciona:
clase estática privada MutableRef {
públicos T conteo;
}
void static main (String [] args) {
definitiva MutableRef mr = nueva MutableRef ();
<. b> mr count = 0;
nueva STARTEND () {
@ Override
public void Oriente () {
para (; mr count <5; mr count + +) {
System.out.println ( "Medio" );
} .
}} doStartEnd ();
System.out.println ( "Count Total" + mr count);}
Salida:
Inicio
de Medio End
Cuenta total 5
Java 7 try-con-recursos característica proporciona mi solución preferida a este tema en particular. No es un verdadero cierre léxico de uso general, pero seguro que se parece a uno para la solución de este problema en particular:
public class ClosureTest {
clase estática privada class="keyword"> implementos AutoCloseable {
público STARTEND ( ) {System.out.println ( "Inicio" );}
@ Override
public void close () {System.out.println ( "End" );}}
void static main (String [] args) {
int count = 0;
tratar (STARTEND se = nueva STARTEND ()) {
para (; neutrófilos <5; cuenta + +) {
System.out.println ( "Medio" );}
} / / fin de STARTEND
System.out.println ( "Count Total" + count);
}}
Un detalle más … Si compila con -Xlint
puede quejar « advertencia: [tratar] de recursos-auto pueda cerrar en sí nunca se hace referencia en el cuerpo de la sentencia try
que corresponde.» He encontrado que el uso de este modelo en mi propio código que suelo utilizar el sí variables dentro del bloque try correspondiente. Pero por las veces que yo no hago,
preventCompilerWarning ()
elimina la advertencia:
public class ClosureTest {
clase estática privada STARTEND implementa AutoCloseable {
público STARTEND () {System.out.println ( "Inicio" );}
@ Override
close () {System.out.println ( "End" );}
class="keyword"> public void preventCompilerWarning () {}
}
void static main (String [] args) {
int count = 0;
tratar (STARTEND se = nueva STARTEND ()) {
se.preventCompilerWarning ();
para (; neutrófilos <5; cuenta + +) {
System.out.println ( "Medio" );}
System.out.println (} = "cadena"> "Count Total" + count);}
}
Voilà - el problema de inicio-fin resuelto! A diferencia de un cierre léxico en una lengua funcional, esta técnica se ejecuta el cierre ()
, incluso cuando se alcanza un retorno
o una excepción
se lanza . Esto puede ser bueno o malo dependiendo de su situación.
cuenta code> está siendo utilizado "dentro" del bloque STARTEND sin ser visible para ese código? Sin la inyección de dependencia, las interfaces, objetos, etc Esta es la esencia de un cierre léxico - una pequeña burbuja de ámbito de variable adicional sin violar la privacidad de otra forma del código cerrado. Esperamos que usted puede imaginar algunos de los paradigmas de codificación versátil que el nuevo bloque try-con-recursos tiene disponibles en Java 7. Para una solución más general al problema de los cierres en Java antes Java 8, echa un vistazo este post.