descargar - prueba/captura vs null check en java
java offline (10)
A veces me enfrento a que debo escribir un fragmento de código como este (normalmente tiene una estructura más compleja y más anidada, pero para el ejemplo es suficiente)
public void printIt(Object1 a){
if (a!=null){
SubObject b= a.getB();
if (b!=null){
SubObject2 c=b.getC();
if(c!=null){
c.print();
}
}
}
}
cuando no necesito saber qué falló y si algo es nulo no hago nada, se podría aplicar un enfoque.
public void printIt(Object1 a){
try{
a.getB().getC().print();
}catch (NullPointerException e) {
}
}
¿Hay algo malo en esta segunda forma, como el rendimiento u otro tipo de problemas?
Gracias
Definitivamente (a), pero debe reestructurar el método para evitar anidar las declaraciones if como se mencionó en una respuesta anterior. Las excepciones no son el impacto en el rendimiento que una vez fueron, pero son mucho más lentos que la comprobación de nulos y nunca se deben usar para el control de flujo de programas como este. Si un objeto puede ser nulo, debe verificarlo, pero si no está permitido, debe fallar rápidamente en el punto en el que asignó la referencia del objeto. En muchas circunstancias, puede tener implementaciones predeterminadas (la lista vacía es un buen ejemplo) para evitar nulos por completo, lo que resulta en un código mucho más limpio. Evita los nulos siempre que puedas.
La parte más equivocada de la segunda versión es que cuando ocurre una NPE dentro de getB()
, getC()
se ignorará silenciosamente. Como ya se mencionó, las excepciones son para casos excepcionales.
La respuesta es usar la versión A.
En general, se considera "mal diseño" el uso de Excepciones para el control de flujo. Las excepciones son para los "excepcionales", especialmente los NPE que son totalmente evitables mediante el uso de cheques nulos. Además, al usar controles nulos, puede indicar (es decir, registrar) qué término es nulo (no sabrá dónde está el nulo con su versión B).
Tenga en cuenta que el rendimiento ya no es un problema, ya que las excepciones de lanzamiento (el seguimiento de la pila, por ejemplo, solo se genera si lo utiliza). Es una cuestión de código limpio.
Sin embargo, hay algunos casos en los que el uso de excepciones para el control de flujo es inevitable, por ejemplo, las excepciones lanzadas desde SimpleDateFormat.parse (), porque no hay una manera razonable de saber antes de realizar la llamada que su entrada no es analizable.
La versión de excepción (¿es similar a las cadenas que usan el operador de navegación segura de Groovy ?.
) Hace que sea realmente fácil tomar la Ley de Deméter (o como yo la llamo, la Sugerencia de palabras fuertes de Deméter) y hacerla tu juguete para la noche.
De manera similar, las declaraciones- if
están profundamente anidadas conducen a un código difícil de leer, y debajo de todo esto, existe la misma "violación", y la complejidad ciclomática de tales métodos es alta.
public void printIt(Object1 a) {
if (null == a) {
return;
}
SubObject b = a.getB();
if (null == b) {
return;
}
SubObject2 c = b.getC();
if (null == c) {
return;
}
c.print();
}
Prefiero ver LAMs (Pequeños métodos auxiliares) en lugares apropiados que encapsulan los cheques y eliminan la necesidad de la pregunta por completo.
Sí. La segunda versión tendrá un rendimiento terrible.
No use excepciones para el flujo de control normal. Artículo 57 de Java efectivo: use excepciones solo para situaciones excepcionales.
== ACTUALIZACIÓN ==
Incluso ignorando los problemas de rendimiento (las excepciones son más rápidas de lo que alguna vez lo fueron, según mi benchmark , pero no tan rápidas como las simples si se comprueban), es realmente improbable usar excepciones para el flujo de programas estándar como este. El bytecode de JVM tiene optimizaciones especiales que puede hacer para verificaciones nulas, incluso en sentencias if
. El primer ejemplo de código es muy preferido.
Si migra a Java 8 puede usar Optional y Lambdas. Primero, debe volver a escribir sus clases para devolver Optional
de cada tipo:
class Object1 {
private SubObject b;
Optional<SubObject> getB() {
return Optional.ofNullable(b);
}
}
class SubObject {
private SubObject2 c;
Optional<SubObject2> getC() {
return Optional.ofNullable(c);
}
}
class SubObject2 {
@Override
public String toString() {
return "to be printed";
}
}
Ahora, puede encadenar las llamadas sin el riesgo de NullPointerExceptions de una manera concisa:
a.getB()
.flatMap(SubObject::getC)
.ifPresent(System.out::println);
Vea el artículo de Oracle ¿ Cansado de excepciones de puntero nulo? Considere usar Java SE 8''s Opcional! para más información.
Un código nunca debe incluir controladores de excepciones para las excepciones no verificadas. Siempre se debe utilizar una comprobación de nulos para una referencia de objeto que tenga la posibilidad de ser nulo.
Noroi
Usando Java 8 opcional:
Optional.ofNullable(a)
.map(Object1::getB)
.map(SubObject::getC)
.ifPresent(Object2::print);
Usar excepciones siempre es una mala idea en términos de rendimiento, sin importar cuán lento sea el mecanismo que solía ser y cómo es ahora. Cuando se lanza una excepción, la pila completa se desenrollará para crear la traza de la pila. Por lo tanto, como dijo Lois Wasserman, no debe confiar en ellos para el flujo del programa (regular) sino en casos excepcionales.
Los ifs anidados no son la definición de belleza, pero le darán la posibilidad de imprimir información adicional como "B es nulo", etc.
public void printIt(Object1 a){
if(a==null){
throw new IllegatArgumentException("a was null, but this is not allowed here."),
}
[...]
Falla rápido y falla duro. Si un shoud no es nulo, lanza una excepción. Esto hará que su código sea más estable y confiable.
Entonces, si tuviera que decidir entre su a) y su b), elegiría a). Pero si no debe ser nulo allí, ocultaría una situación de error.