¿Cuáles son algunos ejemplos de la vida real para comprender el papel clave de las afirmaciones?
java
assertions
Praveen
fuente
fuente
Respuestas:
Se agregaron aserciones (por medio de la palabra clave afirmar ) en Java 1.4. Se utilizan para verificar la corrección de una invariante en el código. Nunca deben activarse en el código de producción, y son indicativos de un error o mal uso de una ruta de código. Se pueden activar en tiempo de ejecución mediante la
-ea
opción deljava
comando, pero no se activan de forma predeterminada.Un ejemplo:
fuente
assert
para verificar los parámetros de métodos públicos ( docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html ). Eso debería arrojar un enException
lugar de matar el programa.This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError.
docs.oracle.com/javase/8/docs/technotes/guides/language/...Supongamos que se supone que debes escribir un programa para controlar una central nuclear. Es bastante obvio que incluso el error más pequeño podría tener resultados catastróficos, por lo tanto, su código debe ser libre de errores (suponiendo que la JVM esté libre de errores por el argumento).
Java no es un lenguaje verificable, lo que significa que no puede calcular que el resultado de su operación será perfecto. La razón principal de esto son los punteros: pueden apuntar a cualquier parte o en ninguna parte, por lo tanto, no se puede calcular que tengan este valor exacto, al menos no dentro de un rango razonable de código. Dado este problema, no hay forma de demostrar que su código es correcto en su conjunto. Pero lo que puede hacer es demostrar que al menos encuentra cada error cuando sucede.
Esta idea se basa en el paradigma de diseño por contrato (DbC): primero define (con precisión matemática) lo que se supone que debe hacer su método y luego lo verifica probándolo durante la ejecución real. Ejemplo:
Si bien es bastante obvio que funciona bien, la mayoría de los programadores no verán el error oculto dentro de este (pista: el Ariane V se bloqueó debido a un error similar). Ahora DbC define que siempre debe verificar la entrada y salida de una función para verificar que funcionó correctamente. Java puede hacer esto mediante aserciones:
Si esta función fallara alguna vez, lo notará. Sabrás que hay un problema en tu código, sabes dónde está y sabes qué lo causó (similar a Excepciones). Y lo que es aún más importante: deja de ejecutar correctamente cuando sucede para evitar que cualquier código adicional funcione con valores incorrectos y potencialmente causar daños a lo que controla.
Las excepciones de Java son un concepto similar, pero no pueden verificar todo. Si desea incluso más comprobaciones (a costa de la velocidad de ejecución), debe usar aserciones. Hacerlo hinchará su código, pero al final puede entregar un producto en un tiempo de desarrollo sorprendentemente corto (cuanto antes solucione un error, menor será el costo). Y además: si hay algún error dentro de su código, lo detectará. No hay forma de que un error se deslice y cause problemas más adelante.
Esto todavía no es una garantía para un código libre de errores, pero está mucho más cerca de eso que los programas habituales.
fuente
new IllegalArgumentException
con el mensaje? Quiero decir, aparte de tener que agregarthrows
a la declaración del método y al código para administrar esa excepción en otro lugar. ¿Por quéassert
insetad de lanzar una nueva excepción? ¿O por qué no un enif
lugar deassert
? Realmente no puedo entender esto :(a
puede ser negativa. La segunda afirmación es inútil; para valores int, siempre es el caso que a + b - b == a. Esa prueba solo puede fallar si la computadora está fundamentalmente rota. Para defenderse de esa contingencia, debe verificar la coherencia en varias CPU.Las aserciones son una herramienta de fase de desarrollo para detectar errores en su código. Están diseñados para eliminarse fácilmente, por lo que no existirán en el código de producción. Por lo tanto, las afirmaciones no son parte de la "solución" que entrega al cliente. Son verificaciones internas para asegurarse de que las suposiciones que está haciendo sean correctas. El ejemplo más común es probar nulo. Muchos métodos se escriben así:
Muy a menudo en un método como este, el widget simplemente nunca debería ser nulo. Entonces, si es nulo, hay un error en su código en algún lugar que necesita rastrear. Pero el código anterior nunca te dirá esto. Entonces, en un esfuerzo bien intencionado para escribir código "seguro", también está ocultando un error. Es mucho mejor escribir código como este:
De esta manera, se asegurará de detectar este error temprano. (También es útil especificar en el contrato que este parámetro nunca debe ser nulo). Asegúrese de activar las aserciones cuando pruebe su código durante el desarrollo. (Y persuadir a sus colegas para que hagan esto también es a menudo difícil, lo que me parece muy molesto).
Ahora, algunos de sus colegas se opondrán a este código, argumentando que aún debe poner el cheque nulo para evitar una excepción en la producción. En ese caso, la afirmación sigue siendo útil. Puedes escribirlo así:
De esta manera, sus colegas estarán felices de que la verificación nula esté allí para el código de producción, pero durante el desarrollo, ya no estará ocultando el error cuando el widget esté nulo.
Aquí hay un ejemplo del mundo real: una vez escribí un método que comparó dos valores arbitrarios para la igualdad, donde cualquiera de los valores podría ser nulo:
Este código delega el trabajo del
equals()
método en el caso en que thisValue no sea nulo. Pero supone que elequals()
método cumple correctamente el contratoequals()
al manejar adecuadamente un parámetro nulo.Un colega se opuso a mi código, diciéndome que muchas de nuestras clases tienen
equals()
métodos con errores que no prueban nulo, por lo que debería poner esa verificación en este método. Es discutible si esto es sabio, o si debemos forzar el error, para que podamos detectarlo y solucionarlo, pero lo aplacé a mi colega y puse un cheque nulo, que he marcado con un comentario:La verificación adicional aquí,
other != null
solo es necesaria si elequals()
método no puede verificar nulo como lo requiere su contrato.En lugar de entablar un debate infructuoso con mi colega sobre la sabiduría de dejar que el código con errores permanezca en nuestra base de código, simplemente pongo dos afirmaciones en el código. Estas afirmaciones me harán saber, durante la fase de desarrollo, si una de nuestras clases no se implementa
equals()
correctamente, para que pueda solucionarlo:Los puntos importantes a tener en cuenta son estos:
Las afirmaciones son solo herramientas de fase de desarrollo.
El objetivo de una afirmación es hacerle saber si hay un error, no solo en su código, sino en su base de código . (Las afirmaciones aquí en realidad marcarán errores en otras clases).
Incluso si mi colega confiara en que nuestras clases fueron escritas correctamente, las afirmaciones aquí aún serían útiles. Se agregarán nuevas clases que podrían fallar al probar null, y este método puede marcar esos errores por nosotros.
En el desarrollo, siempre debe activar las aserciones, incluso si el código que ha escrito no usa aserciones. Mi IDE está configurado para hacer esto siempre de forma predeterminada para cualquier nuevo ejecutable.
Las afirmaciones no cambian el comportamiento del código en producción, por lo que mi colega está feliz de que la verificación nula esté allí y de que este método se ejecute correctamente incluso si el
equals()
método tiene errores. Estoy feliz porque detectaré cualquierequals()
método defectuoso en desarrollo.Además, debe probar su política de aserción colocando una aserción temporal que fallará, por lo que puede estar seguro de que se le notificará, ya sea a través del archivo de registro o un seguimiento de la pila en la secuencia de salida.
fuente
Muchas buenas respuestas explican lo que hace la
assert
palabra clave, pero pocas responden la pregunta real, "¿cuándo debeassert
usarse la palabra clave en la vida real?"La respuesta: casi nunca .
Las afirmaciones, como concepto, son maravillosas. Un buen código tiene muchas
if (...) throw ...
declaraciones (y a sus familiares les gustaObjects.requireNonNull
yMath.addExact
). Sin embargo, ciertas decisiones de diseño han limitado enormemente la utilidad de laassert
palabra clave en sí.La idea principal detrás de la
assert
palabra clave es la optimización prematura, y la característica principal es poder desactivar fácilmente todas las comprobaciones. De hecho, lasassert
verificaciones están desactivadas por defecto.Sin embargo, es de vital importancia que los controles invariantes continúen realizándose en producción. Esto se debe a que la cobertura de prueba perfecta es imposible, y todo el código de producción tendrá errores cuyas afirmaciones deberían ayudar a diagnosticar y mitigar.
Por lo tanto, se
if (...) throw ...
debe preferir el uso de , tal como se requiere para verificar los valores de los parámetros de los métodos públicos y para el lanzamientoIllegalArgumentException
.Ocasionalmente, uno podría verse tentado a escribir un cheque invariable que demore un tiempo indeseablemente largo (y se lo llama con la frecuencia suficiente para que tenga importancia). Sin embargo, dichos controles retrasarán las pruebas, lo que tampoco es deseable. Estos controles que requieren mucho tiempo generalmente se escriben como pruebas unitarias. Sin embargo, a veces puede tener sentido usar
assert
por este motivo.No lo use
assert
simplemente porque es más limpio y más bonito queif (...) throw ...
(y lo digo con mucho dolor, porque me gusta limpio y bonito). Si no puede evitarlo y puede controlar cómo se inicia su aplicación, no dude en utilizarla,assert
pero siempre habilite las afirmaciones en producción. Es cierto que esto es lo que tiendo a hacer. Estoy presionando por una anotación de lombok que haráassert
que actúe más comoif (...) throw ...
. Vota por eso aquí.(Rant: los desarrolladores de JVM eran un montón de codificadores horribles y de optimización prematura. Es por eso que escuchaste sobre tantos problemas de seguridad en el complemento de Java y JVM. Se negaron a incluir comprobaciones básicas y afirmaciones en el código de producción, y seguimos paga el precio.)
fuente
catch (Throwable t)
. No hay ninguna razón para no intentar atrapar, iniciar sesión o volver a intentar / recuperar de OutOfMemoryError, AssertionError, etc.assert
palabra clave es mala. Editaré mi respuesta para que quede más claro que me refiero a la palabra clave, no al concepto.Este es el caso de uso más común. Supongamos que está cambiando un valor de enumeración:
Mientras manejes cada caso, estás bien. Pero algún día, alguien agregará higo a su enumeración y olvidará agregarlo a su declaración de cambio. Esto produce un error que puede ser difícil de detectar, porque los efectos no se sentirán hasta después de que haya dejado la declaración de cambio. Pero si escribe su interruptor de esta manera, puede atraparlo de inmediato:
fuente
AssertionError
si las aserciones están habilitadas (-ea
). ¿Cuál es el comportamiento deseado en la producción? ¿Un no-op silencioso y un posible desastre más adelante en la ejecución? Probablemente no. Sugeriría un explícitothrow new AssertionError("Missing enum value: " + fruit);
.default
para que el compilador pueda advertirle sobre casos faltantes. Puede enreturn
lugar debreak
(esto puede necesitar extraer el método) y luego manejar el caso que falta después del cambio. De esta manera obtienes tanto la advertencia como la oportunidadassert
.Las afirmaciones se utilizan para verificar las condiciones posteriores y las condiciones previas "nunca deben fallar". El código correcto nunca debe fallar en una afirmación; cuando se disparan, deben indicar un error (con suerte en un lugar que esté cerca del lugar real del problema).
Un ejemplo de una afirmación podría ser verificar que un grupo particular de métodos se llame en el orden correcto (por ejemplo, que
hasNext()
se llama antesnext()
en unIterator
).fuente
Veamos el bytecode compilado.
Concluiremos que:
genera casi el mismo bytecode que:
donde
Assert.class.desiredAssertionStatus()
estrue
cuando-ea
se pasa en la línea de comando, y falso de lo contrario.Usamos
System.currentTimeMillis()
para asegurarnos de que no se optimice (loassert true;
hizo).El campo sintético se genera de modo que Java solo necesita llamar
Assert.class.desiredAssertionStatus()
una vez en el momento de la carga, y luego almacena en caché el resultado allí. Ver también: ¿Cuál es el significado de "sintético estático"?Podemos verificar eso con:
Con Oracle JDK 1.8.0_45, se generó un campo estático sintético (consulte también: ¿Cuál es el significado de "sintético estático"? ):
junto con un inicializador estático:
y el método principal es:
Concluimos que:
assert
: es un concepto de lenguaje Javaassert
podría emularse bastante bien con las propiedades del sistema-Pcom.me.assert=true
para reemplazar-ea
en la línea de comando, y athrow new AssertionError()
.fuente
catch (Throwable t)
cláusula también puede detectar violaciones de afirmación? Para mí, eso limita su utilidad solo en el caso en que el cuerpo de la afirmación lleva mucho tiempo, lo cual es raro.AssertionError
primero y volver a lanzarlo.Un ejemplo del mundo real, de una clase Stack (de Assertion in Java Articles )
fuente
Una aserción permite detectar defectos en el código. Puede activar las aserciones para probar y depurar mientras las deja fuera cuando su programa esté en producción.
¿Por qué afirmar algo cuando sabes que es verdad? Solo es cierto cuando todo funciona correctamente. Si el programa tiene un defecto, podría no ser cierto. Detectar esto antes en el proceso le permite saber que algo está mal.
Una
assert
declaración contiene esta declaración junto con unString
mensaje opcional .La sintaxis para una declaración de aserción tiene dos formas:
Aquí hay algunas reglas básicas que rigen dónde se deben usar las afirmaciones y dónde no se deben usar. Las afirmaciones deben usarse para:
Validación de parámetros de entrada de un método privado. NO para métodos públicos.
public
Los métodos deben arrojar excepciones regulares cuando se pasan parámetros incorrectos.En cualquier lugar del programa para garantizar la validez de un hecho que casi con certeza es cierto.
Por ejemplo, si está seguro de que solo será 1 o 2, puede usar una afirmación como esta:
Las afirmaciones no deben usarse para:
Validación de parámetros de entrada de un método público. Dado que las aserciones no siempre se ejecutan, se debe utilizar el mecanismo de excepción regular.
Validar restricciones en algo que es ingresado por el usuario. Lo mismo que arriba.
No debe usarse para efectos secundarios.
Por ejemplo, este no es un uso apropiado porque aquí la aserción se usa por su efecto secundario de llamar al
doSomething()
método.El único caso en el que esto podría justificarse es cuando intenta averiguar si las aserciones están habilitadas o no en su código:
fuente
Además de todas las excelentes respuestas proporcionadas aquí, la guía de programación oficial de Java SE 7 tiene un manual bastante conciso sobre el uso
assert
; con varios ejemplos puntuales de cuándo es una buena (y, lo que es más importante, una mala) idea usar afirmaciones, y cómo es diferente de lanzar excepciones.Enlace
fuente
Assert es muy útil cuando se desarrolla. Lo usas cuando algo simplemente no puede suceder si su código funciona correctamente. Es fácil de usar y puede permanecer en el código para siempre, ya que se desactivará en la vida real.
Si existe alguna posibilidad de que la condición pueda ocurrir en la vida real, entonces debe manejarla.
Me encanta, pero no sé cómo activarlo en Eclipse / Android / ADT. Parece estar apagado incluso cuando se depura. (Hay un hilo sobre esto, pero se refiere al 'Java vm', que no aparece en la Configuración de ejecución ADT).
fuente
Aquí hay una afirmación que escribí en un servidor para un proyecto Hibernate / SQL. Un bean de entidad tenía dos propiedades booleanas efectivas, llamadas isActive e isDefault. Cada uno podría tener un valor de "Y" o "N" o nulo, que se trató como "N". Queremos asegurarnos de que el cliente del navegador esté limitado a estos tres valores. Entonces, en mis setters para estas dos propiedades, agregué esta afirmación:
Observe lo siguiente.
Esta afirmación es solo para la fase de desarrollo. Si el cliente envía un valor incorrecto, lo detectaremos temprano y lo arreglaremos, mucho antes de llegar a la producción. Las afirmaciones son por defectos que puede detectar temprano.
Esta afirmación es lenta e ineficiente. Esta bien. Las afirmaciones son libres de ser lentas. No nos importa porque son herramientas solo de desarrollo. Esto no ralentizará el código de producción porque las aserciones estarán deshabilitadas. (Hay cierto desacuerdo sobre este punto, que trataré más adelante). Esto me lleva al siguiente punto.
Esta afirmación no tiene efectos secundarios. Podría haber probado mi valor contra un conjunto final estático no modificable, pero ese conjunto se habría mantenido en producción, donde nunca se usaría.
Esta afirmación existe para verificar el correcto funcionamiento del cliente. Entonces, cuando lleguemos a la producción, nos aseguraremos de que el cliente esté funcionando correctamente, de modo que podamos desactivar la afirmación de forma segura.
Algunas personas preguntan esto: si la afirmación no es necesaria en la producción, ¿por qué no simplemente eliminarla cuando haya terminado? Porque aún los necesitará cuando comience a trabajar en la próxima versión.
Algunas personas han argumentado que nunca debes usar afirmaciones, porque nunca puedes estar seguro de que todos los errores hayan desaparecido, por lo que debes mantenerlos disponibles incluso en producción. Por lo tanto, no tiene sentido usar la declaración de aserción, ya que la única ventaja de las afirmaciones es que puede desactivarlas. Por lo tanto, de acuerdo con este pensamiento, usted (casi) nunca debe usar afirmaciones. Estoy en desacuerdo. Ciertamente es cierto que si una prueba pertenece a la producción, no debe usar una afirmación. Pero esta prueba no pertenece a la producción. Este es para detectar un error que probablemente nunca llegue a producción, por lo que puede apagarse de manera segura cuando haya terminado.
Por cierto, podría haberlo escrito así:
Esto está bien solo para tres valores, pero si el número de valores posibles aumenta, la versión HashSet se vuelve más conveniente. Elegí la versión HashSet para hacer mi punto sobre la eficiencia.
fuente
HashSet
aporta ninguna ventaja de la velocidad a través de unaArrayList
. Además, las creaciones de conjunto y lista dominan el tiempo de búsqueda. Estarían bien cuando se usa una constante. Dicho todo esto, +1.Las aserciones se usan básicamente para depurar la aplicación o se usan para reemplazar el manejo de excepciones de alguna aplicación para verificar la validez de una aplicación.
La afirmación funciona en tiempo de ejecución. Un ejemplo simple, que puede explicar todo el concepto de manera muy simple, es aquí: ¿qué hace la palabra clave de aserción en Java? (WikiAnswers).
fuente
Las aserciones están deshabilitadas por defecto. Para habilitarlos debemos ejecutar el programa con
-ea
opciones (se puede variar la granularidad). Por ejemplo,java -ea AssertionsDemo
.Hay dos formatos para usar aserciones:
assert 1==2; // This will raise an AssertionError
.assert 1==2: "no way.. 1 is not equal to 2";
esto generará un AssertionError con el mensaje que se muestra también y, por lo tanto, es mejor. Aunque la sintaxis real esassert expr1:expr2
donde expr2 puede ser cualquier expresión que devuelva un valor, la he usado con más frecuencia solo para imprimir un mensaje.fuente
Para recapitular (y esto es cierto para muchos lenguajes, no solo Java):
"afirmar" se utiliza principalmente como una ayuda de depuración por los desarrolladores de software durante el proceso de depuración. Los mensajes de afirmación nunca deberían aparecer. Muchos idiomas proporcionan una opción de tiempo de compilación que hará que se ignoren todos los "asertos", para su uso en la generación de código de "producción".
Las "excepciones" son una forma práctica de manejar todo tipo de condiciones de error, ya sea que representen o no errores lógicos, porque, si se encuentra con una condición de error tal que no puede continuar, simplemente puede "lanzarlos al aire, "desde donde sea que estés, esperando que alguien más esté listo para" atraparlos ". El control se transfiere en un solo paso, directamente desde el código que arrojó la excepción, directamente al guante del receptor. (Y el receptor puede ver el seguimiento completo de las llamadas que se han realizado).
Además, las personas que llaman a esa subrutina no tienen que verificar si la subrutina tuvo éxito: "si estamos aquí ahora, debe haber tenido éxito, porque de lo contrario habría arrojado una excepción y no estaríamos aquí ahora". Esta estrategia simple hace que el diseño de código y la depuración sean mucho, mucho más fáciles.
Las excepciones permiten convenientemente que las condiciones de error fatal sean lo que son: "excepciones a la regla". Y, para que sean manejados por una ruta de código que también es "una excepción a la regla ... " ¡fly ball! "
fuente
Las afirmaciones son cheques que pueden desconectarse. Raramente se usan. ¿Por qué?
result != null
ya que estos controles son muy rápidos y casi no hay nada que guardar.Entonces, ¿qué queda? Las comprobaciones costosas para las condiciones realmente se espera que sean ciertas. Un buen ejemplo serían los invariantes de una estructura de datos como RB-tree. En realidad, en
ConcurrentHashMap
JDK8, hay algunas afirmaciones significativas para elTreeNodes
.A veces, el cheque no es realmente costoso, pero al mismo tiempo, está bastante seguro de que pasará. En mi código, por ejemplo, hay
donde estoy bastante seguro de que la lista que acabo de crear tiene elementos únicos, pero quería documentarla y verificarla dos veces.
fuente
Básicamente, "afirmar verdadero" pasará y "afirmar falso" fallará. Veamos cómo funcionará esto:
fuente
assert
es una palabra clave Fue introducido en JDK 1.4. Hay dos tipos deassert
sassert
Declaraciones muy simplesassert
Declaraciones simplesPor defecto
assert
, no se ejecutarán todas las declaraciones. Si unaassert
declaración recibe falso, automáticamente generará un error de aserción.fuente