¿Debería afirmar que no es nulo con la declaración de aserción en el código de producción? [cerrado]

32

He visto esta pregunta pero tengo algunas preguntas más sobre el uso de la assertpalabra clave. Estaba debatiendo con otros codificadores sobre el uso assert. Para este caso de uso, había un método que puede devolver nulo si se cumplen ciertos requisitos previos. El código que escribí llama al método, luego afirma que no devuelve nulo y continúa utilizando el objeto devuelto.

Ejemplo:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

Ahora imagina que lo uso así:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

Me dijeron que debería eliminar el assert, que nunca deberían usarse en el código de producción, solo deberían usarse en las pruebas. ¿Es eso cierto?

Big_Bad_E
fuente
19
Por defecto, las aserciones están deshabilitadas. Debe habilitarlos explícitamente en tiempo de ejecución a través de -eao equivalente.
jarmod
Pregunta relacionada sobre Ingeniería de software : softwareengineering.stackexchange.com/q/137158/187318 (aunque es bastante antigua, por lo que la respuesta aceptada allí todavía recomienda una biblioteca externa, que ya no es necesaria desde la introducción de Objects.requireNonNullJava 8).
Hulk
El título de esta pregunta es demasiado amplio, pero la pregunta formulada en el cuerpo es mucho más estrecha y, según las respuestas, está totalmente manejada por funciones auxiliares.
smci
Para ser claros, está preguntando si el código del cliente debe implementar comprobaciones / afirmaciones no nulas en los objetos. (Eso es diferente de preguntar si el código de la biblioteca debería garantizar que los objetos no pueden ser nulos, o afirmar que allí).
smci

Respuestas:

19

Úselo Objects.requireNonNull(Object)para eso.

Comprueba que la referencia de objeto especificada no es nula. Este método está diseñado principalmente para hacer validación de parámetros en métodos y constructores, [...]

En su caso esto sería:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

Esta función se realiza para los fines que mencionó, es decir, marque explícitamente lo que no debe ser nulo; También en producción. El gran beneficio es que se asegura de encontrar valores nulos justo donde no deberían aparecer en primer lugar. Tendrá menos problemas para depurar los problemas causados ​​por valores nulos que se pasaron a un lugar donde no deberían estar.

Otro beneficio es la flexibilidad adicional con respecto a los cheques nulos en contraste con assert. Si bien assertes una palabra clave para verificar un valor booleano, Objects.requireNonNull(Object)es una función y, por lo tanto, puede integrarse en un código mucho más flexible y legible. P.ej:

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

Tenga en cuenta que Objects.requireNonNull(Object)es exclusivamente para la verificación nula donde assertse generaliza. asserttiene propósitos ligeramente diferentes a ese respecto, es decir, principalmente pruebas. Tiene que estar habilitado, para que pueda habilitarlo para las pruebas y deshabilitarlo para la producción. De todos modos, no lo use para el código de producción. Podría ralentizar la aplicación con validaciones innecesarias y complicadas destinadas a pruebas. Úselo para separar las comprobaciones de solo pruebas de las comprobaciones destinadas a la producción también.

Consulte la documentación oficial para obtener más información assert.

akuzminykh
fuente
14
¿Quién y cuándo declarado afirma ser legado? Cualquier enlace / referencias? No puedo imaginar a ningún desarrollador sensato que defina "heredado" la herramienta que permite validaciones de huella cero que se pueden habilitar fácilmente en las pruebas y deshabilitar en la producción.
Dmitry Pisklov
Dado que usted puede decidir en tiempo de ejecución si afirma operar, pero no puede determinar en tiempo de ejecución si requireNonNull lanza el NPE, ¿qué quiere decir con "tiene más control con él que con afirmar"?
Pete Kirkham
@DmitryPisklov Tienes razón, lo he editado. Es una gran herramienta para probar.
Akuzminykh
2
@PeteKirkham Tienes razón, control era la palabra incorrecta para eso. Estaba hablando más sobre la flexibilidad en la sintaxis. Además: aquí puede encontrar razones por las que desea que el código arroje NPE.
Akuzminykh
1
"no lo use para el código de producción. Podría ralentizar la aplicación" Podría, pero podría valer la pena. Java tiene comprobaciones de tiempo de ejecución integradas para garantizar que nunca sobrepase una matriz. Estos están habilitados incluso en implementaciones de producción, a pesar de la posible penalización de rendimiento. Ni siquiera tenemos una opción al respecto. No siempre es incorrecto tener verificaciones de tiempo de ejecución en el código de producción.
Max Barraclough
22

Lo más importante que debe recordar acerca de las afirmaciones es que se pueden deshabilitar, por lo que nunca suponga que se ejecutarán.

Por compatibilidad con versiones anteriores, la JVM deshabilita la validación de aserción de forma predeterminada. Deben habilitarse explícitamente utilizando el argumento de línea de comando -enableassertions o su abreviatura -ea:

java -ea com.whatever.assertion.Assertion

Por lo tanto, no es una buena práctica confiar en ellos.

Como las aserciones no están habilitadas por defecto, nunca puede asumir que se ejecutarán cuando se usen en el código. Por lo tanto, siempre debe verificar los valores nulos y los opcionales vacíos, evite usar aserciones para verificar las entradas en un método público y, en su lugar, use una excepción no verificada ... En general, haga todas las verificaciones como si la aserción no estuviera allí.

jeprubio
fuente
Obviamente, es una mala práctica confiar en ellos, pero ¿es una mala práctica usarlo en general?
Big_Bad_E
3
@Big_Bad_E Assertions es una herramienta de depuración que existe para hacer que un programa falle lo antes posible y con el mayor ruido posible. Es entonces el trabajo del conjunto de pruebas asegurarse de que no haya forma de que un programa pueda fallar a pesar de las afirmaciones . La idea es que, una vez que el conjunto de pruebas (tiene una cobertura del 100%, ¿no es así?!?) Se ejecuta sin fallas, es seguro eliminar las afirmaciones ya que no pueden activarse de todos modos. Claro que hay un poco de idealismo en este razonamiento, pero esa es la idea.
cmaster - reinstalar monica
11

Seguramente lo que te dicen es una mentira descarada. Este es el por qué.

Las aserciones están deshabilitadas de manera predeterminada si solo ejecuta jvm independiente. Cuando están deshabilitados, tienen cero huella, por lo tanto, no afectarán su aplicación de producción. Sin embargo, probablemente sean sus mejores amigos cuando desarrollen y prueben su código, y la mayoría de los corredores de framework de prueba habilitan aserciones (JUnit lo hace), por lo que su código de aserción se ejecuta cuando ejecuta sus pruebas unitarias, lo que lo ayuda a detectar cualquier error potencial antes (por ejemplo, puede agregar afirmaciones para algunas comprobaciones de límites de lógica empresarial, y eso ayudará a detectar algún código que use valores inapropiados).

Dicho esto, como sugiere la otra respuesta, exactamente por esa razón (no siempre están habilitados) no puede confiar en las afirmaciones para hacer algunas comprobaciones vitales, o (¡especialmente!) Mantener cualquier estado.

Para ver un ejemplo interesante de cómo podría usar afirmaciones, eche un vistazo aquí : al final del archivo hay un método singleThreadedAccess()que se llama desde la declaración de aserción en la línea 201 y está allí para detectar cualquier acceso multiproceso potencial en las pruebas.

Dmitry Pisklov
fuente
4

Las otras respuestas ya cubren esto bastante bien, pero hay otras opciones.

Por ejemplo, Spring tiene un método estático:

org.springframework.util.Assert.notNull(obj)

También hay otras bibliotecas con sus propios Assert.something()métodos. También es bastante simple escribir el tuyo.

Sin embargo, tenga en cuenta qué excepciones lanza si se trata de un servicio web. El método anterior mencionado, por ejemplo, arroja unIllegalArgumentException que por defecto en Spring devuelve un 500.

En el caso de un servicio web, esto a menudo no es un error interno del servidor, y no debe ser un 500, sino un 400, que es una mala solicitud.

Christopher Schneider
fuente
1
Si el método "Afirmar" no bloquea inmediatamente el proceso, arrojando algún tipo de excepción, entonces no es una afirmación. El objetivo principal assertes obtener un bloqueo inmediato con un archivo central producido para que pueda realizar una autopsia con su depurador favorito justo en el lugar donde se violó la condición.
cmaster - reinstalar monica
Las afirmaciones no bloquean inmediatamente un proceso. Lanzan un error, que puede ser atrapado. Las excepciones no controladas bloquearán sus pruebas al igual que los errores. Puedes discutir la semántica si quieres. Si lo desea, escriba una afirmación personalizada que arroje un error en lugar de una excepción. El hecho es que, en la abrumadora mayoría de los casos, la acción apropiada es lanzar una excepción en lugar de un error.
Christopher Schneider
Ah, Java realmente define assertlanzar. Interesante. La versión C / C ++ no hace tal cosa. Inmediatamente genera una señal de que a) mata el proceso yb) crea un volcado de núcleo. Lo hace por una razón: es muy simple depurar una falla de aserción porque todavía tiene toda la información disponible sobre la pila de llamadas. La definición assertde lanzar una excepción en su lugar, que luego puede ser atrapada (no) intencionalmente mediante programación, derrota el propósito, en mi humilde opinión.
cmaster - reinstalar monica
Así como hay algunas (o muchas) cosas raras en C y C ++, hay algunas en Java. Uno de ellos es Throwable. Siempre pueden ser atrapados. Si eso es bueno o no, no lo sé. Supongo que depende. Muchos programas Java son servicios web, y sería indeseable que se bloquee por casi cualquier motivo, por lo que casi todo se captura y se registra. Una de las mejores cosas es el seguimiento de la pila, y eso suele ser suficiente para diagnosticar el motivo de una excepción o error por sí mismo.
Christopher Schneider
3

El uso afirma libremente cuando hacerlo ayuda a detectar errores de programación, es decir, errores.

No use asir para atrapar algo que podría suceder lógicamente, es decir, una entrada mal formateada. Use afirmar solo cuando el error sea irrecuperable.

No coloque ninguna lógica de producción en el código que se ejecuta cuando se verifica la aserción. Si su software está bien escrito, esto es trivialmente cierto, pero si no es así, podría tener efectos secundarios sutiles y un comportamiento general diferente con afirmaciones habilitadas y deshabilitadas.

Si su empresa tiene "código de prueba" y "código de producción" haciendo lo mismo pero como diferentes bases de código (o diferentes etapas de edición), salga de allí y nunca regrese. Intentar arreglar ese nivel de incompetencia es probablemente una pérdida de tiempo. Si su empresa no pone ninguna declaración de afirmación fuera del código de las pruebas, dígales amablemente que las afirmaciones están deshabilitadas en la compilación de producción y que si no lo están, corregir su error es ahora su primera prioridad.

El valor de las afirmaciones debe usarse precisamente dentro de la lógica empresarial y no solo en el conjunto de pruebas. Esto facilita la producción de muchas pruebas de alto nivel que no tienen que probar explícitamente muchas cosas para pasar por grandes fragmentos de su código y desencadenar todas estas afirmaciones. En algunos de mis proyectos, las pruebas típicas ni siquiera afirmaron nada, simplemente ordenaron que se realizara un cálculo basado en datos específicos y esto provocó que se verificaran cientos de afirmaciones y se encontraran problemas incluso en pequeñas piezas de lógica en el fondo.

Kafein
fuente
2

Puede usar afirmar en cualquier momento. El debate que viene es cuándo usar. Por ejemplo en la guía :

  • No utilice aserciones para la comprobación de argumentos en métodos públicos.
  • No utilice aserciones para realizar cualquier trabajo que su aplicación requiera para su correcto funcionamiento.
Gatusko
fuente
44
Buenas reglas Pero la respuesta sería mejor con algunas razones añadidas.
cmaster - reinstalar monica