¿Debería un método de recuperación devolver 'nulo' o lanzar una excepción cuando no puede producir el valor de retorno? [cerrado]

StackOverflow https://stackoverflow.com/questions/175532

  •  05-07-2019
  •  | 
  •  

Pregunta

Tengo un método que se supone que devuelve un objeto si se encuentra.

Si no se encuentra, debería I:

  1. devolver nulo
  2. lanzar una excepción
  3. otro
¿Fue útil?

Solución

Si siempre está esperando encontrar un valor, lance la excepción si falta. La excepción significaría que hubo un problema.

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

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

Otros consejos

Solo lanza una excepción si es realmente un error. Si es el comportamiento esperado para que el objeto no exista, devuelva el nulo.

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

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

Hagas lo que hagas, te recomiendo que no realices la tercera opción: devolver una cadena que diga " WTF " ;.

Si null nunca indica un error, simplemente devuelve null.

Si el valor nulo es siempre un error, lance una excepción.

Si null es a veces una excepción, codifique dos rutinas. Una rutina lanza 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 no se encontró el objeto.

Es difícil usar mal una rutina de prueba. Es muy fácil olvidarse de verificar si hay un valor nulo.

Entonces, cuando nulo es un error, simplemente escribes

object o = FindObject();

Cuando el nulo no es un error, puedes codificar algo como

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

Solo quería recapitular las opciones mencionadas anteriormente, lanzando algunas nuevas en:

  1. devolver nulo
  2. lanzar una excepción
  3. usar el patrón de objeto nulo
  4. proporcione un parámetro booleano a su método, de modo que el llamante pueda elegir si desea que lance una excepción
  5. proporcione un parámetro adicional, para que la persona que llama pueda establecer un valor que recupere si no se encuentra ningún valor

O puedes combinar estas opciones:

Proporcione varias versiones sobrecargadas de su receptor, para que la persona que llama pueda decidir qué camino tomar. En la mayoría de los casos, solo el primero tiene una implementación del algoritmo de búsqueda, y los otros solo se ajustan al primero:

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

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

No debe abusar de él, pero puede ser útil, especialmente cuando escribe una clase auxiliar que usará en cientos de aplicaciones diferentes con muchas convenciones diferentes de manejo de errores.

Use el patrón de objeto nulo o lance una excepción.

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

Solo pregúntate: "¿es un caso excepcional que no se encuentra el objeto " ;? Si se espera que suceda en el curso normal de su programa, probablemente no debería generar una excepción (ya que no es un comportamiento excepcional).

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

-Alan.

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

LBYL dice que debe verificar los valores (devuelva un valor nulo)
EAFP dice que solo debe intentar la operación y ver si falla (lanzar una excepción)

aunque estoy de acuerdo con lo anterior ... las excepciones deben usarse para condiciones de error / excepcionales, y devolver un nulo es mejor cuando se usan cheques.


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

Ventajas de lanzar una excepción:

  1. Flujo de control más limpio en su código de llamada. La comprobación de nula inyecta una rama condicional que se maneja de forma nativa mediante try / catch. La comprobación de nulo no indica qué es lo que está comprobando; está comprobando si está buscando un error que está esperando, o si está comprobando que no se pasa más en el downchain. ?
  2. Elimina la ambigüedad de lo que " null " significa. ¿Es nulo representativo de un error o es nulo lo que realmente se almacena en el valor? Es difícil decirlo cuando solo tienes una cosa para basar esa determinación.
  3. Mejor consistencia entre el comportamiento del método en una aplicación. Las excepciones suelen estar expuestas en las firmas de los métodos, por lo que es más capaz de comprender en qué casos de borde los métodos de una cuenta de la aplicación y qué información La aplicación puede reaccionar de manera predecible.

Para obtener más explicaciones con ejemplos, consulte: http : //metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/

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

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

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

2) solo una entrada es válida, es decir, la que resulta en un objeto encontrado. En cuyo caso DEBE ofrecer un segundo método que permita a la persona que llama determinar si su entrada será correcta. Por ejemplo

is_present(key)
find(key) throws Exception

SI y SOLAMENTE SI proporciona los dos métodos del segundo contrato, puede lanzar una excepción si no se encuentra nada.

Prefiero devolver solo un valor nulo y confiar en la persona que llama para que lo maneje adecuadamente. La excepción (por falta de una palabra mejor) es si estoy absolutamente "seguro" de que este método devolverá un objeto. En ese caso, un fallo es un excepcional debe y debe lanzarse.

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

Si es un estado normal de cosas, devuelve nulo. Esto es algo que puede suceder de vez en cuando, y las personas que llaman deben buscarlo.

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

En última instancia, cualquiera de los dos funcionaría, aunque la mayoría de las personas generalmente consideran una buena práctica usar solo Excepciones cuando algo, bueno, Excepcional ha sucedido.

Aquí hay un par de sugerencias más.

Si devuelve una colección, evite devolver un valor nulo, devuelva una colección vacía que haga que la enumeración sea más fácil de tratar sin un control nulo primero.

Varias API de .NET utilizan el patrón de un parámetro thrownOnError que le da a la persona que llama la opción de si realmente es una situación excepcional o no si no se encuentra el objeto. 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 puede considerar el patrón de Objeto nulo en algunas circunstancias, que puede ser un valor predeterminado o una versión sin comportamiento. La clave es evitar las comprobaciones nulas en todo el código base. Consulte aquí para obtener más información http://geekswithblogs.net/dsellers/archive /2006/09/08/90656.aspx

En algunas funciones agrego un parámetro:

..., bool verify = true)

Verdadero significa lanzar, falso significa devolver un valor de error de retorno. De esta manera, quienquiera que use esta función tiene ambas opciones. El valor predeterminado debe ser verdadero, para el beneficio de aquellos que se olvidan del manejo de errores.

Devuelva un nulo en lugar de lanzar una excepción y documente 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 resulte en algún tipo de " excepción de puntero nulo " de todos modos :)

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

Opción A

Object *findObject(Key &key);

Devuelve nulo cuando no se puede encontrar un objeto. Agradable y sencillo. Yo iría con este. Los enfoques alternativos a continuación son para las personas que no odian a los que están fuera de control.

Opción B

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

Pase una referencia a la variable que recibirá el objeto. El método arrojó una excepción cuando no se puede encontrar un objeto. Esta convención probablemente sea más adecuada si realmente no se espera que no se encuentre un objeto; por lo tanto, se 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 no se puede encontrar un objeto. La ventaja de esto sobre la opción A es que puede verificar el caso de error en un paso claro:

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

refiriéndose solo al caso donde null no se considera un comportamiento excepcional, definitivamente estoy a favor del método de prueba, está claro, no hay necesidad de " leer el libro " o " mira antes de saltar " como se dijo 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()
}
...

Si es importante que el código del cliente sepa la diferencia entre lo que se encuentra y lo que no se encuentra y se supone que este es un comportamiento de rutina, entonces es mejor devolver el valor nulo. El código del cliente puede decidir qué hacer.

Por lo general, debe devolver nulo. El código que llama al método debe decidir si lanzar una excepción o intentar otra cosa.

O devolver una opción

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

Luego tiene métodos como T getOrElse (T valueIfNull) en este objeto para devolver el objeto encontrado o una alternativa a las especificaciones del cliente.

Desafortunadamente, JDK es inconsistente, si intenta acceder a una clave no existente en el paquete de recursos, no se encuentra la excepción y cuando solicita un valor del mapa, obtiene un valor nulo si no existe. Así que cambiaría la respuesta del ganador a la siguiente, si el valor encontrado puede ser nulo, luego generar una excepción cuando no se encuentre, de lo contrario, devuelva nulo. Entonces, siga la regla con una excepción, si necesita saber por qué no se encuentra el valor, siempre genere la excepción, o ...

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

Sin embargo, si está devolviendo todo el asunto sangriento (como en C ++ si lo haces: 'return blah;' en lugar de 'return & amp; blah;' (o 'blah' es un puntero), entonces no puedes regresar un NULO, porque no es del tipo 'objeto'. En ese caso, lanzar una excepción o devolver un objeto en blanco que no tenga un conjunto de indicadores de éxito es la forma en que abordaría el problema.

No creo que nadie haya mencionado la sobrecarga en el manejo de excepciones: requiere recursos adicionales para cargar y procesar la excepción, de modo que, a menos que sea una verdadera aplicación o un evento que detenga el proceso (seguir adelante causaría más daño que bien) optaría por devolviendo un valor que el entorno de llamada podría interpretar como mejor le parezca.

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

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

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

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

Prefiero volver nulo -

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

Si la persona que llama realmente no lo usa, no le cobres impuestos por el try / catch

Si el método devuelve una colección, devuelva una colección vacía (como se dijo anteriormente). Pero por favor, no colecciones.EMPTY_LIST o tal! (en el caso de Java)

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

  1. Si el método siempre debe encontrar el resultado y es un caso de excepción real no encontrar el objeto, entonces debe lanzar una excepción (en Java: por favor, una Excepción no verificada)
  2. (solo Java) Si puede tolerar que el método arroje una excepción marcada, lance una excepción ObjectNotFoundException específica 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 no se encuentra el objeto y su nombre de Método es como findBookForAuthorOrReturnNull (..), entonces puede devolver el valor nulo. En este caso, se recomienda fuertemente el uso de algún tipo de comprobación estática o compilación, que evita la desreferenciación del resultado sin una comprobación nula. En el caso de Java puede ser por ejemplo. FindBugs (consulte DefaultAnnotation en http://findbugs.sourceforge.net/manual/annotations.html) o IntelliJ-Checking.

Ten cuidado, si decides devolver un nulo. Si no es el único programador en el proyecto, obtendrá NullPointerExceptions (en Java o lo que sea en otros idiomas) en tiempo de ejecución. Por lo tanto, no devuelva los valores nulos que no se verifican en el momento de la compilación.

Si está utilizando una biblioteca u otra clase que lanza una excepción, debe volver a hacerlo . 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) un seguimiento de la pila para el 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();
    }
}
}

Ejemplo.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;
        }

    }
}

Ejemplo2.java

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

}

Eso realmente depende de si esperas encontrar el objeto o no. Si sigue la escuela de pensamiento que se deben usar excepciones para indicar algo, bueno, err, excepcional ha ocurrido entonces:

  • objeto encontrado; devuelve el objeto
  • Objeto no encontrado; lanzar excepción

De lo contrario, devuelve nulo.

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