Lanzar una excepción o retorno nulo

Si tengo la función de abajo, con dos opciones.

private MyObject findBlank() { for (int i = 0; i < pieces.length; i++) { if(pieces[i].isBlank()){ return pieces[i]; } } return null; } private MyObject findBlank() { for (int i = 0; i < pieces.length; i++) { if(pieces[i].isBlank()){ return pieces[i]; } } throw new NoSuchFieldError("No blank piece found!"); } 

Por este método, sé que siempre debería devolver un objeto, una de las ‘piezas’ siempre es isBlank() == true , el retorno nulo al final es solo para complacer al comstackdor. Dado que este es el caso y mi código no funcionaría de todos modos si devolviera un valor nulo, ¿es correcto enviar una excepción?

Mis opciones son:

  1. devuelve nulo y la aplicación obtendrá una NullPointerException en algún caso perimetral
  2. devuelve null y envuelve el uso del método con (myObject! = null) cheques
  3. lanzar una excepción que lo hará estallar en tiempo de ejecución

Supongo que lo que estoy preguntando es si este es el lugar correcto para lanzar una excepción. es decir, no hay nada que pueda hacer al respecto si entra en la situación. ¿Se clasifica como “excepcional” o debo anular lo que devuelve mi método (lo que hace que mi código se vea horrible)? Si sé que no debería devolver el valor nulo, debería lanzar la excepción, ¿no?

Además, ¿cómo elegiré qué excepción, o extenderé una y lanzaré la mía?

Sobre tus opciones, pregúntate si

  1. ¿Es una buena idea hacer que su progtwig explote en algún momento después de que este método devuelva un valor inesperado (es decir, null )?
  2. ¿Qué se ocultará exactamente si enmascara el null retorno null ?
  3. ¿Es una buena idea explotar de inmediato, solo porque hubo un valor incorrecto?

Personalmente optaría por la opción 2 o 3, dependiendo de si me gusta más la respuesta a la pregunta 2 o 3. La opción 1 definitivamente es una mala idea, especialmente si no se supone que suceda. Si el progtwig lanza un NPE mucho después de que regresó su función, tendrá dificultades para averiguar de dónde proviene el null . Especialmente si esto sucede meses después de haber terminado de trabajar en esta función en particular.

Si elige lanzar una excepción, inmediatamente ve donde algo salió mal y puede ir directamente allí para averiguar por qué salió mal. Devolver el null y verificarlo en la función de llamada también podría funcionar, pero solo si no falla silenciosamente, pero en realidad hace algo para manejar el problema adecuadamente.

Supongo que lo que estoy preguntando es si este es el lugar correcto para lanzar una excepción.

Si fuera una situación excepcional , entonces sí. Si se espera la posibilidad de no encontrar nada que coincida con los criterios, entonces la situación no es excepcional y debe devolver el null .

Sí, debe lanzar una excepción RuntimeException para indicar una situación “excepcional” que no debería haber ocurrido. IllegalStateException probablemente se ajuste a la factura. Asegúrese de incluir un mensaje con cualquier información que le ayude a encontrar el error si alguna vez se lanza.

Si devuelve el valor nulo, la mayor parte del tiempo recurrirá a la información perdida en la vista del contrato, el consumidor no puede saber cuál es el motivo de la respuesta incorrecta si obtiene el nulo del productor.

Mirando su primer código, hay dos situaciones en las que el código externo obtiene la excepción NULLPointerException: 1. Las piezas son nulas 2. Las piezas no tenían tal elemento

Por lo tanto, devolver el valor nulo desviará el código externo para una operación posterior, generará un problema potencial.

Y si hablamos de la diferencia entre devolver nullObject (no null) y excepción, la principal diferencia es la PROBABILIDAD, que significa: 1. Si la situación vacía es mucho más probable, debería devolver nullObject para que TODO el código externo pueda / deba manejarlos explícitamente . 2. Si la situación vacía es menos probable, ¿por qué no lanzar la excepción para que la función de llamada final pueda manejarlo directamente?

Siempre debe devolver un objeto o devolver un valor nulo.
y
la aplicación obtendrá una NullPointerException en algún caso de ventaja
esos dos son contradictorios

Si está realmente seguro de que siempre tiene pieces[i].isBlank() , lance IllegalStateException

De lo contrario manejar el caso de acuerdo a sus necesidades.

Si la matriz siempre debe tener un valor válido para devolver, debe generar una excepción como alternativa. (2d caso en su ejemplo)

Eventualmente, puede declarar su propia clase (excepción) de excepción.

Sugeriría usar el tipo de datos Maybe (también conocido como Option ) del que acabo de hablar en otra respuesta hace un tiempo.

Este tipo de datos está disponible en Java funcional .

Uso:

 private Option findBlank() { for (int i = 0; i < pieces.length; i++) { if(pieces[i].isBlank()){ return Option.some(pieces[i]); } } return Option.none(); } 

Nota al margen:

Su método findBack puede generalizarse a un método que toma un predicado como un argumento y encuentra y devuelve el primer elemento que lo satisface.

Como era de esperar, Functional Java ya tiene eso también .

Supongamos por un momento que las pieces son fj.data.List . Entonces tu método puede ser reescrito como:

 private Option findBlank() { return pieces.find(new F1() { public Boolean f(MyObject p) { return p.isBlank(); } }); } 

Otra nota al margen:

Tal vez el código anterior se ve bastante retorcido. El "cierre plegable" de IntelliJ IDEA puede ser de alguna ayuda aquí .

Puede ser una buena idea usar un patrón de Objeto nulo .

Provide an object as a surrogate for the lack of an object of a given type. The Null Object provides intelligent do nothing behavior, hiding the details from its collaborators

Entonces, en ese caso, no tendrá que usar excepciones o devolver el valor nulo. Siempre puede devolver el objeto de tipo de retorno deseado. El truco es cuando no tiene nada que devolver, en lugar de devolver un null o lanzar una excepción, puede devolver el Null object que es del mismo tipo que el tipo de retorno deseado.

Esta documentación tiene algunos ejemplos y descripciones. Y tiene un caso similar al tuyo, resuelto por el patrón de diseño.

 public class CustomerFactory { public static final String[] names = {"Rob", "Joe", "Julie"}; public static AbstractCustomer getCustomer(String name){ for (int i = 0; i < names.length; i++) { if (names[i].equalsIgnoreCase(name)){ return new RealCustomer(name); } } return new NullCustomer(); } } 

Aparte de la mayoría de las respuestas, me gustaría señalar que si el desempeño es su preocupación, las excepciones son mucho más lentas que las devoluciones nulas.

Echa un vistazo a este código:

 class Main { public static void main(String[] args) { testException(); testNull(); } public static void testException() { long st = System.currentTimeMillis(); for(int i=0;i<10000000;i++) { try{ exp(); } catch(Exception e) { } } long et = System.currentTimeMillis(); System.out.println("Time taken with exceptions : "+(et-st)); } public static void testNull() { long st = System.currentTimeMillis(); for(int i=0;i<10000000;i++) { returnNull(); } long et = System.currentTimeMillis(); System.out.println("Time taken with null : "+(et-st)); } public static void exp() throws Exception { throw new Exception(); } public static Object returnNull() { return null; } } 

Los resultados en mi máquina son:

 Time taken with exceptions : 7526 Time taken with exceptions : 5 

Si la excepción de lanzamiento es una condición rara en su código y no ocurrirá con frecuencia, el tiempo empleado en ambas situaciones es casi el mismo.

Tendrá que hacer que el rendimiento frente a la capacidad de mantenimiento / legibilidad se reduzca .

Lea más sobre esto aquí