Pregunta

Estoy leyendo el excelente Código limpio

Una discusión es sobre pasar valores nulos a un método.

public class MetricsCalculator {
    public double xProjection(Point p1, Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}
...
calculator.xProjection(null, new Point(12,13));

Representa diferentes formas de manejar esto:

public double xProjection(Point p1, Point p2) {
    if (p1 == null || p2 == null) {
        throw new IllegalArgumentException("Invalid argument for xProjection");
    }
    return (p2.x - p1.x) * 1.5;
}

public double xProjection(Point p1, Point p2) {
    assert p1 != null : "p1 should not be null";
    assert p2 != null : "p2 should not be null";
    return (p2.x - p1.x) * 1.5;
}

Prefiero el afirmaciones enfoque, pero no me gusta el hecho de que las afirmaciones estén desactivadas de forma predeterminada.

El libro finalmente dice:

En la mayoría de los lenguajes de programación no existe una buena manera de lidiar con un valor nulo que una persona que llama pasa accidentalmente.Dado que este es el caso, el enfoque racional es prohibir pasar null de forma predeterminada.

¿Realmente no entra en cómo harías cumplir esta restricción?

¿Alguno de ustedes tiene opiniones firmes de cualquier manera?

¿Fue útil?

Solución

Tanto el uso de aserciones como el lanzamiento de excepciones son enfoques válidos aquí.Cualquiera de los mecanismos se puede utilizar para indicar un error de programación, no un error de tiempo de ejecución, como es el caso aquí.

  • Las aserciones tienen la ventaja del rendimiento, ya que normalmente están deshabilitadas en los sistemas de producción.
  • Las excepciones tienen la ventaja de la seguridad, ya que el control se realiza siempre.

La elección realmente depende de las prácticas de desarrollo del proyecto.El proyecto en su conjunto necesita decidir sobre una política de afirmación:Si la opción es habilitar las aserciones durante todo el desarrollo, entonces yo diría que se usen aserciones para verificar este tipo de parámetro no válido: en un sistema de producción, es poco probable que una NullPointerException lanzada debido a un error de programación pueda detectarse y manejarse. de manera significativa de todos modos y por lo tanto actuará como una afirmación.

Sin embargo, en la práctica, conozco a muchos desarrolladores que no confían en que las afirmaciones se habiliten cuando sea apropiado y, por lo tanto, optan por la seguridad de lanzar una NullPointerException.

Por supuesto, si no puede aplicar una política para su código (si está creando una biblioteca, por ejemplo, y depende de cómo otros desarrolladores ejecutan su código), debe optar por el enfoque seguro de lanzar NullPointerException para esos métodos que forman parte de la API de la biblioteca.

Otros consejos

La regla general es que si su método no espera null argumentos entonces deberías tirar System.ArgumentNullException.Lanzar correctamente Exception no solo lo protege de la corrupción de recursos y otras cosas malas, sino que también sirve como guía para los usuarios de su código, ahorrando tiempo dedicado a depurarlo.

Lea también un artículo sobre Programación defensiva

Tampoco es de uso inmediato, pero está relacionado con la mención de Spec#...Existe una propuesta para agregar "tipos seguros para nulos" a una versión futura de Java: "Manejo de nulos mejorado: tipos seguros para nulos".

Según la propuesta, su método se convertiría en

public class MetricsCalculator {
    public double xProjection(#Point p1, #Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}

dónde #Point es el tipo de no-null referencias a objetos de tipo Point.

¿Realmente no entra en cómo harías cumplir esta restricción?

Lo haces cumplir lanzando un ArgumentoExcepción si pasan en nulo.

if (p1 == null || p2 == null) {
    throw new IllegalArgumentException("Invalid argument for xProjection");
}

Prefiero el uso de afirmaciones.

Tengo la regla de que solo uso aserciones en métodos públicos y protegidos.Esto se debe a que creo que el método de llamada debe garantizar que pasa argumentos válidos a métodos privados.

¡El número de especificación parece muy interesante!

Cuando algo así no está disponible, generalmente pruebo métodos no privados con una verificación nula en tiempo de ejecución y aserciones para métodos internos.En lugar de codificar la verificación nula explícitamente en cada método, lo delego a una clase de utilidades con un método de verificación nula:

/**
 * Checks to see if an object is null, and if so 
 * generates an IllegalArgumentException with a fitting message.
 * 
 * @param o The object to check against null.
 * @param name The name of the object, used to format the exception message
 *
 * @throws IllegalArgumentException if o is null.
 */
public static void checkNull(Object o, String name) 
    throws IllegalArgumentException {
   if (null == o)
      throw new IllegalArgumentException(name + " must not be null");
}

public static void checkNull(Object o) throws IllegalArgumentException {
   checkNull(o, "object");
} 

// untested:
public static void checkNull(Object... os) throws IllegalArgumentException {
   for(Object o in os) checkNull(o);  
}

Entonces la verificación se convierte en:

public void someFun(String val1, String val2) throws IllegalArgumentException {
   ExceptionUtilities.checkNull(val1, "val1");
   ExceptionUtilities.checkNull(val2, "val2");

   /** alternatively:
   ExceptionUtilities.checkNull(val1, val2);
   **/

   /** ... **/
} 

Eso se puede agregar con macros de editor o un script de procesamiento de código.Editar: La verificación detallada también podría agregarse de esta manera, pero creo que es mucho más fácil automatizar la adición de una sola línea.

En la mayoría de los lenguajes de programación no existe una buena manera de lidiar con un valor nulo que una persona que llama pasa accidentalmente.Dado que este es el caso, el enfoque racional es prohibir pasar null de forma predeterminada.

encontré JetBrains' @Nullable y @NotNull El enfoque de anotaciones para abordar esto es el más ingenioso hasta el momento.Desafortunadamente, es específico de IDE, pero en mi opinión es realmente limpio y potente.

http://www.jetbrains.com/idea/documentation/howto.html

Tener esto (o algo similar) como estándar de Java sería realmente bueno.

Aunque no está estrictamente relacionado, es posible que quieras echar un vistazo a Especificaciones#.

Creo que todavía está en desarrollo (por parte de Microsoft), pero hay algunos CTP disponibles y parece prometedor.Básicamente te permite hacer esto:

  public static int Divide(int x, int y)
    requires y != 0 otherwise ArgumentException; 
  {
  }

o

  public static int Subtract(int x, int y)
    requires x > y;
    ensures result > y;
  {
    return x - y;
  } 

También proporciona otras características como los tipos Notnull.Está construido sobre .NET Framework 2.0 y es totalmente compatible.La sintaxis, como puedes ver, es C#.

@Chris Karcher Yo diría absolutamente correcto.Lo único que diría es verificar los parámetros por separado y hacer que la excepción informe el parámetro que era nulo también, ya que facilita mucho el seguimiento de dónde proviene el nulo.

@wvdschel ¡guau!Si escribir el código supone demasiado esfuerzo para usted, debería buscar algo como PostSharp (o un equivalente de Java si hay uno disponible) que puede posprocesar sus ensamblajes e insertar comprobaciones de parámetros por usted.

Dado que lo fuera de tema parece haberse convertido en el tema, Scala adopta un enfoque interesante al respecto.Se supone que todos los tipos no son nulos, a menos que los envuelva explícitamente en un Option para indicar que podría ser nulo.Entonces:

//  allocate null
var name : Option[String]
name = None

//  allocate a value
name = Any["Hello"]

//  print the value if we can
name match {
  Any[x] => print x
  _ => print "Nothing at all"
}

Generalmente prefiero no hacer ninguna de las dos cosas, ya que solo ralentiza las cosas.Las NullPointerExceptions se lanzan más adelante de todos modos, lo que rápidamente llevará al usuario a descubrir que está pasando un valor nulo al método.Solía ​​​​verificar, pero el 40% de mi código terminó siendo código de verificación, momento en el que decidí que simplemente no valía la pena recibir mensajes de afirmación agradables.

Estoy de acuerdo o en desacuerdo con publicación de wvdschel, depende de lo que esté diciendo específicamente.

En este caso, claro, este método fallará null por lo que probablemente no sea necesaria la verificación explícita aquí.

Sin embargo, si el método simplemente almacena los datos pasados ​​y hay algún otro método al que llamas más tarde que se encargará de ello, Descubrir entradas incorrectas lo antes posible es la clave para corregir errores más rápido.En ese momento posterior, podría haber innumerables formas en que se entregaran datos incorrectos a su clase.Es como tratar de descubrir cómo las ratas entraron a tu casa después del hecho, tratar de encontrar el agujero en alguna parte.

Ligeramente fuera de tema, pero una característica de encontrar errores Lo que creo que es muy útil es poder anotar los parámetros de los métodos para describir a qué parámetros no se les debe pasar un valor nulo.

Utilizando el análisis estático de su código, encontrar errores Luego puede señalar ubicaciones donde se llama al método con un valor potencialmente nulo.

Esto tiene dos ventajas:

  1. La anotación describe su intención sobre cómo se debe llamar el método, lo que ayuda a la documentación.
  2. FindBugs puede señalar posibles problemas con el método, lo que le permite rastrear posibles errores.

Sólo es útil cuando tienes acceso al código que llama a tus métodos, pero ese suele ser el caso.

Lanzando C# ArgumentException, o Java IllegalArgumentException Justo al principio del método me parece la solución más clara.

Siempre se debe tener cuidado con las excepciones de tiempo de ejecución: excepciones que no se declaran en la firma del método.Dado que el compilador no le obliga a detectarlos, es muy fácil olvidarse de ellos.Asegúrese de tener algún tipo de manejo de excepciones "captura todas" para evitar que el software se detenga abruptamente.Esa es la parte más importante de su experiencia de usuario.

La mejor manera de manejar esto sería el uso de excepciones.En última instancia, las afirmaciones terminarán dando una similar experiencia para el usuario final, pero no proporciona ninguna forma para que el desarrollador que llame a su código maneje la situación antes de mostrar una excepción al usuario final.En última instancia, desea asegurarse de probar entradas no válidas lo antes posible (especialmente en código público) y proporcionar las excepciones apropiadas que el código de llamada pueda detectar.

En Java, suponiendo que el valor nulo proviene de un error de programación (es decir,nunca debe salir de la fase de prueba), luego deje que el sistema lo ejecute, o si hay efectos secundarios al llegar a ese punto, verifique si hay nulo al principio y genere IllegalArgumentException o NullPointerException.

Si la nula pudiera provenir de una realidad excepciónAl caso, pero no desea utilizar una excepción marcada para eso, entonces definitivamente desea seguir la ruta IllegalArgumentException al comienzo del método.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top