2008-10-06 17 views
412

Tengo un método que se supone devuelve un objeto si se encuentra.¿Debe un método de recuperación devolver 'nulo' o lanzar una excepción cuando no puede producir el valor de retorno?

Si no se encuentra, en caso de que:

  1. nula rentabilidad
  2. lanzar una excepción
  3. otra
+47

lo que haga, asegúrese de documentarlo. Creo que este punto es más importante que exactamente qué enfoque es el "mejor". – Rik

+6

Esto depende de las expresiones idiomáticas prevalecientes en el lenguaje de programación. Por favor marque esta pregunta con una etiqueta de lenguaje de programación. – Teddy

+2

La devolución nula solo puede significar éxito o falla, que a menudo no es mucha información (algunos métodos pueden fallar de muchas maneras). Las bibliotecas deberían arrojar excepciones para hacer explícitos los errores y de esta manera el programa principal puede decidir cómo manejar el error en un nivel superior (en contraste con la lógica de manejo de errores incorporada). –

Respuesta

409

Si siempre está esperando para encontrar un valor a continuación, iniciar la excepción si no se encuentra. La excepción significaría que hubo un problema.

Si el valor puede estar presente o presente y ambos son válidos para la lógica de aplicación a continuación, devolver un nulo.

Más importante: ¿Qué haces en otros lugares del código? La consistencia es importante.

+25

@Ken: +1, sería bueno mencionar que si lanza una excepción, pero puede detectarla a priori (como HasItem (...)), entonces el el usuario debe proporcionar dicho método Has * o Contiene. – user7116

+0

Si lanza excepciones en los métodos del modelo, obliga al desarrollador a desarrollar bloques try/catch desde el alcance de la llamada, lo que garantiza que las rutinas que llaman a esos métodos del modelo se manejan adecuadamente desde ese alcance. De lo contrario, el desarrollador podría hacer lo que quiera. Considérelo una interfaz para llamar a los métodos del modelo, si lo desea. Si los datos no están allí, es probable que tenga que hacer alguna solución de todos modos. – axiom82

+3

En lugar de devolver un valor o null cuando falta el valor, considere devolver un Maybe . Ver http://mikehadlow.blogspot.nl/2011/01/monads-in-c-5-maybe.html. –

4

prefiero simplemente devolver un valor nulo, y confiar en la persona que llama para manejarlo apropiadamente La excepción (a falta de una mejor palabra) es que si estoy absolutamente "seguro" de que este método devolverá un objeto. En ese caso, una falla es excepcional y debería arrojarse.

13

Sea consistente con la (s) API (s) que está utilizando.

17

Usa el patrón de objeto nulo o lanza una excepción.

+0

Esta es la verdadera respuesta. Regresar nulo es un hábito terrible de programadores perezosos. – jeremyjjbrown

+0

No puedo creer que esta respuesta aún no se haya votado a la cima. Esta es la respuesta real, y cualquiera de los dos enfoques es dead-easy y ahorra una gran cantidad de código inflado o NPE. – Bane

+0

http://sourcemaking.com/design_patterns/null_object – Bane

2

Si es importante para el código de cliente para reconocer la diferencia entre los encontró y no se encuentra y esto se supone que es un comportamiento rutinario, entonces lo mejor es volver nula. El código del cliente puede decidir qué hacer.

2

Generalmente debe devolver nulo. El código que llama al método debe decidir si lanzar una excepción o intentar algo más.

0

Eso realmente depende de si espera encontrar el objeto, o no. Si sigue la línea de pensamiento de que deben usarse excepciones para indicar algo, bueno, err, excepcional ha ocurrido entonces:

  • Objeto encontrado; objeto de devolución
  • Objeto no encontrado; excepción tiro

De lo contrario, devolver null.

88

Solo eche una excepción si es realmente un error. Si se espera que el comportamiento del objeto no exista, devuelva el valor nulo.

De lo contrario, es una cuestión de preferencia.

+0

No estoy de acuerdo. Puede lanzar excepciones como códigos de estado: "NotFoundException" – ACV

55

Como regla general, si el método siempre debe devolver un objeto, y luego ir con la excepción. Si anticipa el nulo ocasional y desea manejarlo de cierta manera, vaya con el nulo.

lo que haga, me altamente desaconsejar la tercera opción: Devolución de una cadena que dice "WTF".

+0

Más uno porque en los viejos tiempos lo hice más de un par de veces como una rápida solución sucia "temporal" ... no es una buena idea. Especialmente si será revisado si eres un estudiante. – rciafardone

+9

Iba a votar porque las opciones WTF me parecían increíbles ... pero al parecer tengo un corazón – swestner

+0

throw new WtfExceptin – Kamafeather

4

Depende de lo que signifique que no se encuentra el objeto.

Si se trata de un estado normal, devuelva nulo.Esto es algo que podría suceder de vez en cuando, y los que llaman deberían verificarlo.

Si se trata de un error, genere una excepción, las personas que llaman deben decidir qué hacer con la condición de error del objeto faltante.

En última instancia cualquiera funcionaría, aunque la mayoría de la gente generalmente considera que es una buena práctica usar excepciones solo cuando algo, bueno, excepcional ha sucedido.

0

Depende de la naturaleza del método y cómo se usará. Si es un comportamiento normal que el objeto no se encuentre, entonces devuelve nulo. Si es un comportamiento normal que el objeto siempre se encuentre, lanza una excepción.

Como regla general, use excepciones solo cuando se produzca algo excepcional . No escriba el código de tal forma que la excepción de arrojar y atrapar sea parte de su operación normal.

1

Siempre que se suponga que debe devolver una referencia al objeto, devolver un NULL debería ser bueno.

Sin embargo, si devuelve todo el asunto sangriento (como en C++ si lo haces: 'return blah;' en lugar de 'return &blah;' (o 'blah' es un puntero), entonces no puedes devolver un NULL , porque no es del tipo 'objeto'. En ese caso, lanzar una excepción o devolver un objeto en blanco que no tiene un indicador de éxito establecido es cómo abordaría el problema.

0

Si no lo encuentra es una evento excepcional (es decir, debe estar allí en circunstancias normales), luego lanzar. De lo contrario, devolver un valor "no encontrado" (puede ser nulo, pero no tiene que), o incluso hacer que el método devuelva un booleano para encontrado/no encontrado y un parámetro de salida para el objeto real.

0

Eso depende de tu método. Si se supone que su método siempre devuelve un objeto válido y no se encuentra ninguno, lanzar una excepción es el camino a seguir. Si el método es justo para devolver un objeto que podría o no estar allí (como quizás una imagen en una persona de contacto), no debería generar un error.

También puede ser que desee para exponer un método que devuelve un valor booleano verdadero/falso si este método en realidad va a devolver un objeto por lo que no tiene que ser a) comprobar la nula o b) capturar una excepción

0

El La opción "other" podría ser permitir que el método find tome un parámetro adicional con un objeto predeterminado que se devolvería si no se puede encontrar el objeto buscado.

De lo contrario, simplemente devolvería nulo a menos que realmente sea un caso excepcional cuando no se encuentre el objeto.

3

Devuelve un nulo en lugar de lanzar una excepción y documentar claramente la posibilidad de un valor de retorno nulo en la documentación de la API. Si el código de llamada no respeta la API y busca el caso nulo, lo más probable es que produzca algún tipo de "excepción de puntero nulo" de todos modos :)

En C++, puedo pensar en 3 sabores diferentes de configuración un método que encuentra un objeto.

Opción A

Object *findObject(Key &key); 

nulo retorno cuando un objeto no puede ser encontrado. Agradable y simple. Yo iría con este. Los enfoques alternativos a continuación son para personas que no odian a los paramáticos.

Opción B

void findObject(Key &key, Object &found); 

Pass en una referencia a la variable que va a recibir el objeto. El método arroja una excepción cuando no se puede encontrar un objeto. Esta convención es probablemente más adecuada si realmente no se espera que un objeto no se encuentre; por lo tanto, lanza una excepción para indicar que se trata de un caso inesperado.

Opción C

bool findObject(Key &key, Object &found); 

El método devuelve falso cuando un objeto no puede ser encontrado. La ventaja de esta opción sobre A es que se puede comprobar en el caso de error en un solo paso claro:

if (!findObject(myKey, myObj)) { ... 
1

No creo que nadie ha mencionado la sobrecarga en el manejo de excepciones - tiene recursos adicionales para cargar y procesar la excepción así que, a menos que sea un verdadero evento de detención de aplicaciones o proceso (en adelante, causaría más daño que bien), optaría por devolver un valor que el entorno de llamadas podría interpretar como lo considere oportuno.

1

Estoy de acuerdo con lo que parece ser el consenso aquí (devolver null si "no encontrado" es un resultado normal posible, o lanzar una excepción si la semántica de la situación requiere que el objeto siempre se encontrará).

Hay, sin embargo, una tercera posibilidad que podría tener sentido en función de su situación particular. Su método podría devolver un objeto predeterminado de algún tipo en la condición "no encontrado", lo que permite que el código de llamada tenga la seguridad de que siempre recibirá un objeto válido sin la necesidad de una comprobación nula o una captura de excepciones.

0

En código de la capa de datos, I algunas veces utilizar el siguiente código, permitiendo que la persona que llama para decidir si "objeto no encontrado" significa que ha ocurrido un error.


DataType GetObject(DBConnection conn, string id, bool throwOnNotFound) { 
    DataType retval = ... // find object in database 
    if (retval != null || ! throwOnNotFound) { 
     return retval; 
    } else { 
     throw new NoRowsFoundException("DataType object with id {id} not found in database"); 
    } 
} 

DataType GetObject(DBConnection conn, string id) { 
    return GetObject(conn, id, true); 
} 
45

Si null nunca indica un error, simplemente devuelve nulo.

Si null es siempre un error, genere una excepción.

Si nula es a veces una excepción a continuación código de dos rutinas. Una rutina arroja una excepción y la otra es una rutina de prueba booleana que devuelve el objeto en un parámetro de salida y la rutina devuelve un falso si el objeto no se encontró.

Es difícil utilizar incorrectamente una rutina de prueba. Es realmente fácil olvidarse de verificar nulo.

Así que cuando nulo es un error que acaba de escribir

object o = FindObject(); 

Cuando el nulo no es un error que puede codificar algo así como

if (TryFindObject(out object o) 
    // Do something with o 
else 
    // o was not found 
+1

Esta sería una sugerencia más útil si C# proporcionara tuplas reales, por lo que podríamos evitar el uso de [out ] parámetro. Aún así, este es el patrón preferido, entonces +1. –

+2

En mi opinión, el enfoque try es el mejor. No tiene que mirar hacia arriba, entonces, ¿qué sucede si el objeto no puede ser devuelto? Con un método de prueba, inmediatamente sabes qué hacer. – OregonGhost

2

O devolver una opción

Una opción es básicamente una clase de contenedor que obliga al cliente a manejar casos de stand. Scala tiene este concepto, busca su API.

Luego tiene métodos como T getOrElse (T valueIfNull) en este objeto, ya sea devuelve el objeto encontrado, o un allternative las especificaciones del cliente.

1

Devuelve un nulo, las excepciones son exactamente eso: algo que su código hace que no se espera.

10

depende si su idioma y código promueve: LBYL (mirar antes de saltar) o EAFP (más fácil pedir perdón que permiso)

LBYL dice que usted debe comprobar si los valores (por lo devuelva un valor nulo)
EAFP dice que acaba de intentar la operación y ver si se produce un error (una excepción)

aunque estoy de acuerdo con la anterior .. excepciones deben ser utilizados para condiciones excepcionales/error y devolver un valor nulo es mejor cuando el uso de cheques.


EAFP vs LBYL en Python:
http://mail.python.org/pipermail/python-list/2003-May/205182.html (Web Archive)

+1

En algunos casos, EAFP es el único enfoque significativo. En un mapa/diccionario concurrente, por ejemplo, no hay forma de preguntar si existirá una asignación cuando se solicite. – supercat

3

En algunas funciones que añadir un parámetro:

..., bool verify = true) 

medios verdaderos tiran, mediante falsas devolver algunos de retorno de error valor. De esta manera, quien usa esta función tiene ambas opciones. El valor predeterminado debe ser verdadero, para el beneficio de aquellos que olvidan el manejo de errores.

0

No contiene el objeto puede suceder durante las operaciones normales y debe ser tratado por la persona que llama return NULL.

Si no contiene el objeto que indica un error por el código de llamada o el estado interno, realice una afirmación.

Si no contiene el objeto, indica un evento infrecuente. (Al igual que alguien borró un artículo de la tienda mientras estaba revisando el artículo al mismo tiempo). Luego, haga una excepción.

12

Simplemente pregúntese: "¿es un caso excepcional que el objeto no se encuentra"? Si se espera que suceda en el curso normal de su programa, probablemente no deba hacer una excepción (ya que no es un comportamiento excepcional).

Versión corta: use excepciones para manejar un comportamiento excepcional, no para manejar el flujo normal de control en su programa.

-Alan.

1

Las excepciones deben ser excepcional. Devuelve nulo si es válido para devolver un nulo.

+1

Más o menos cierto. Ver http://www.boost.org/community/error_handling.html –

5

Las excepciones están relacionadas con Diseño por contrato.

La interfaz de un objeto es en realidad un contrato entre dos objetos, el llamante debe cumplir el contrato o el receptor puede fallar con una excepción. Hay dos posibles contratos

1) todas las entradas el método es válido, en cuyo caso debe devolver nulo cuando no se encuentra el objeto.

2) solo algunas entradas son válidas, es decir, las que dan como resultado un objeto encontrado. En cuyo caso DEBE ofrecer un segundo método que le permita a la persona que llama determinar si su entrada será correcta. Por ejemplo

is_present(key) 
find(key) throws Exception 

si y sólo si usted proporciona dos métodos de la segunda contrato, se le permite lanzar una excepción se se encontró nada!

4

Aquí hay un par de sugerencias más.

Si devuelve una colección, evite devolver nula, devuelva una colección vacía que hace que la enumeración sea más fácil de tratar sin una comprobación nula primero.

Varios .NET API usan el patrón de un parámetro throwwnOnError que le da a la persona que llama la opción de si realmente se trata de una situación excepcional o no si el objeto no se encuentra. Type.GetType es un ejemplo de esto. Otro patrón común con BCL es el patrón TryGet donde se devuelve un valor booleano y el valor se pasa a través de un parámetro de salida.

También podría considerar el patrón Objeto nulo en algunas circunstancias que pueden ser un valor predeterminado o una versión sin comportamiento. La clave es evitar las comprobaciones nulas en toda la base de códigos. Vea aquí para más información http://geekswithblogs.net/dsellers/archive/2006/09/08/90656.aspx

22

Sólo quería recapitular las opciones mencionadas antes, lanzando algunos nuevos en:

  1. devuelto nulo
  2. lanzar una excepción
  3. utilizar el patrón de objeto nulo
  4. proporciona un parámetro booleano para su método, por lo que la persona que llama puede elegir si desea lanzar una excepción
  5. proporcionan un parámetro adicional, por lo que la persona que llama puede establecer un valor que vuelva si no se encuentra ningún valor

O usted podría combinar estas opciones:

proporcionan varias versiones sobrecargadas de su comprador, por lo que la persona que llama puede decidir qué camino tomar. En la mayoría de los casos, sólo el primero tiene una implementación del algoritmo de búsqueda, y los otros que se acaban de envolver alrededor de la primera:

Object findObjectOrNull(String key); 
Object findObjectOrThrow(String key) throws SomeException; 
Object findObjectOrCreate(String key, SomeClass dataNeededToCreateNewObject); 
Object findObjectOrDefault(String key, Object defaultReturnValue); 

Incluso si usted elige para proporcionar una sola aplicación, es posible que desee utilizar una nombrando una convención como esa para aclarar su contrato, y le ayuda si alguna vez decide agregar otras implementaciones también.

No debe usarlo en exceso, pero puede ser útil, especialmente al escribir una clase auxiliar que utilizará en cientos de aplicaciones diferentes con muchas convenciones de manejo de errores diferentes.

+0

Me gustan los nombres de las funciones claras, especialmente orCreate y orDefault. – marcovtwout

+1

La mayor parte de esto se puede escribir con 'Esperado findObject (String)' donde 'Expected ' tiene las funciones 'orNull()', 'orThrow()', 'oSupplied (Proveedor proveedor)', 'orDefault (T por defecto) '. Esto resume la obtención de los datos del manejo de errores – WorldSEnder

+0

que no sabía sobre el esperado hasta ahora. Parece ser bastante nuevo y podría no haber existido cuando escribí la respuesta original. Tal vez deberías hacer que tu comentario sea una respuesta adecuada. –

3

sólo se refiere al caso en que nulo no se considera un comportamiento excepcional soy definitivamente para el método de prueba, es evidente, no hay necesidad de "leer el libro" o "mirar antes de saltar", como se ha dicho aquí

así que básicamente:

bool TryFindObject(RequestParam request, out ResponseParam response) 

y esto significa que el código del usuario también estará claro

... 
if(TryFindObject(request, out response) 
{ 
    handleSuccess(response) 
} 
else 
{ 
    handleFailure() 
} 
... 
1

Prefiero nula regresar -

Si la persona que llama lo usa sin verificarlo, la excepción ocurre allí de todos modos.

Si la persona que llama en realidad no lo uso, no lo gravar una try/catch bloque

1

excepción Desafortunadamente JDK es inconsistente, si no tratando clave de acceso existente en paquete de recursos, que no te encuentren y cuando solicita valor del mapa obtiene nulo si no existe. Así que cambiaría la respuesta del ganador a lo siguiente, si el valor encontrado puede ser nulo, luego se elevará la excepción cuando no se encuentre, de lo contrario devolverá nulo. Así que siga a la regla con una excepción, si usted necesita saber por qué no se encuentra el valor a continuación, subir siempre una excepción, o ..

4

Ventajas de lanzar una excepción:

  1. Limpiador de flujo de control en su código de llamada Comprobación de nulos inyecta una rama condicional que es tratada nativamente por try/catch. La comprobación de null no indica qué es lo que está comprobando: ¿está buscando nulo porque está buscando un error que está esperando o está buscando nulo para no pasarlo más adelante? ?
  2. Elimina la ambigüedad de lo que significa "nulo". ¿Es nulo el representante de un error o es nulo lo que realmente está almacenado en el valor? Es difícil de decir cuando solo tienes una cosa para basar esa determinación.
  3. Consistencia mejorada entre el comportamiento del método en una aplicación. Las excepciones suelen estar expuestas en las firmas de métodos, por lo que es más capaz de entender qué casos extremos tienen los métodos en una aplicación y a qué información puede reaccionar su aplicación de manera predecible.

Para más explicación con ejemplos, véase: http://metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/

+0

+1 porque el punto 2 es excelente - nulo no tiene el mismo significado que no se encuentra. Esto se vuelve más importante cuando se trata de lenguajes dinámicos donde Null podría ser realmente el objeto almacenado/recuperado por la función, y en ese caso –

1

Si el método devuelve una colección, a continuación, devuelve una colección vacía (como sayed arriba). Pero, por favor, no Collections.EMPTY_LIST o tal. (en el caso de Java)

Si el método recupera un solo objeto, entonces tiene algunas opciones.

  1. Si el método siempre debe buscar el resultado y es un caso de excepción real no puede encontrar el objeto, entonces debería lanzar una excepción (en Java: solicitar una excepción no comprobada)
  2. (Java) Si ha puede tolerar que el método arroje una excepción marcada, arroje un ObjectNotFoundException específico del proyecto o similar. En este caso, el compilador le dice si olvida manejar la excepción. (Este es mi manejo preferido de cosas no encontradas en Java.)
  3. Si dice que está realmente bien, si el objeto no se encuentra y su nombre de método es como findBookForAuthorOrReturnNull (..), puede devolver nulo. En este caso, es fuertemente que se recomienda utilizar algún tipo de comprobación estática o verificación de compilador, lo que impide la desreferenciación del resultado sin una verificación nula. En el caso de Java, puede ser, por ejemplo. FindBugs (ver DefaultAnnotation en http://findbugs.sourceforge.net/manual/annotations.html) o IntelliJ-Checking.

Tenga cuidado, si decide devolver un valor nulo. Si no eres el único programador en el proyecto, obtendrás NullPointerExceptions (en Java o lo que sea en otros idiomas) en el tiempo de ejecución. Por lo tanto, no devuelva nulos que no se verifican en tiempo de compilación.

+0

No si el código se escribió correctamente para esperar 'null'. Vea la respuesta más votadas para más. –

+0

Pero solo si se asegura en tiempo de compilación, que todos los nulos están marcados. Esto se puede hacer utilizando FindBugs @NutNull a nivel de paquete y marque su método como "puede devolver nulo". O para usar un lenguaje como Kotlin o Niza. Pero es mucho más simple no devolver nulo. – iuzuz

+0

"Simpler", * quizás *. Pero a menudo es incorrecto –

1

Si está utilizando una biblioteca u otra clase que arroja una excepción, debe volver a generar. Aquí hay un ejemplo. Example2.java es como una biblioteca y Example.java usa su objeto. Main.java es un ejemplo para manejar esta excepción. Debería mostrar un mensaje significativo y (si es necesario) seguimiento de pila al usuario en el lado de la llamada.

Main.java

public class Main { 
public static void main(String[] args) { 
    Example example = new Example(); 

    try { 
     Example2 obj = example.doExample(); 

     if(obj == null){ 
      System.out.println("Hey object is null!"); 
     } 
    } catch (Exception e) { 
     System.out.println("Congratulations, you caught the exception!"); 
     System.out.println("Here is stack trace:"); 
     e.printStackTrace(); 
    } 
} 
} 

Example.java

/** 
* Example.java 
* @author Seval 
* @date 10/22/2014 
*/ 
public class Example { 
    /** 
    * Returns Example2 object 
    * If there is no Example2 object, throws exception 
    * 
    * @return obj Example2 
    * @throws Exception 
    */ 
    public Example2 doExample() throws Exception { 
     try { 
      // Get the object 
      Example2 obj = new Example2(); 

      return obj; 

     } catch (Exception e) { 
      // Log the exception and rethrow 
      // Log.logException(e); 
      throw e; 
     } 

    } 
} 

Example2.java

/** 
* Example2.java 
* @author Seval 
* 
*/ 
public class Example2 { 
    /** 
    * Constructor of Example2 
    * @throws Exception 
    */ 
    public Example2() throws Exception{ 
     throw new Exception("Please set the \"obj\""); 
    } 

} 
Cuestiones relacionadas