Cierres de Java y el problema Principio-Fin

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 start () y el final () 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:

  1. 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" );
    }
    Esto ha limitado, pero es agradable cuando . funciona
  2. 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;
    }
    ejemplos del mundo real pueden complicarse rápidamente. Alguien en StackExchange: Programadores, dijo recientemente que habitualmente encuentran los procedimientos de 10 y 20 parámetros en las que trabajaban y que «murieron un poco por dentro» cada vez que se encontraron una. Hay muchas maneras de hacer que los insectos con este enfoque: la transposición de valores, confundiendo el valor de aprobación por Java para los tipos por defecto en relación con el paso por … sólo para nombrar unos pocos. El esfuerzo participan de codificación puede ser impresionante.
  3. 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.

Tenga en cuenta que la variable local cuenta 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.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *