Java: if-return-if-return vs if-return-elseif-return
Hizo una pregunta no relacionada donde tenía un código como este:
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
// Check property values
}
Recibí un comentario que afirmaba que esto no era óptimo, y que en su lugar (si entendí correctamente) debería hacer esto:
public boolean equals(Object obj)
{
if (this == obj)
return true;
else if (obj == null)
return false;
else if (getClass() != obj.getClass())
return false;
// Check property values
}
Debido a las declaraciones return, realmente no puedo ver por qué ninguna de ellas debería ser más eficiente o más rápida que la otra. Dado un determinado objeto, ambos métodos tendrían que hacer un número igual de comprobaciones por lo que puedo ver. Y debido a las declaraciones de retorno, no código extra se ejecutaría en cualquiera de ellos.
¿Me estoy perdiendo algo aquí? ¿Hay algo en ello? ¿Hay algunas optimizaciones del compilador o algo pasando o lo que sea?
Sé que esto es micro optimización y lo más probable es que me quede con la primera de cualquier manera, ya que creo que se ve más limpio con todos los ifs en la misma posición. Pero no puedo evitarlo; ¡tengo curiosidad!
5 answers
El código de bytes generado es idéntico para esos dos casos, por lo que es puramente una cuestión de estilo.
Produje dos métodos e1
y e2
y ambos produjeron este código de byte (leído usando javap -v
):
public boolean e1(java.lang.Object); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: aload_1 2: if_acmpne 7 5: iconst_1 6: ireturn 7: aload_1 8: ifnonnull 13 11: iconst_0 12: ireturn 13: aload_0 14: invokevirtual #25; //Method java/lang/Object.getClass:()Ljava/lang/Class; 17: aload_1 18: invokevirtual #25; //Method java/lang/Object.getClass:()Ljava/lang/Class; 21: if_acmpeq 26 24: iconst_0 25: ireturn
Omití el código que puse después de eso para compilarlo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-14 12:51:03
Ninguno de los dos es más eficiente que el otro. El compilador puede ver fácilmente que los dos son idénticos, y de hecho Suns/Oracles javac
produce bytecode idéntico para los dos métodos.
Aquí hay una clase IfTest
:
class IfTest {
public boolean eq1(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
return true;
}
public boolean eq2(Object obj) {
if (this == obj)
return true;
else if (obj == null)
return false;
else if (getClass() != obj.getClass())
return false;
return true;
}
}
Lo compilé con javac
y el desmontaje es el siguiente:
public boolean eq1(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: if_acmpne 7
5: iconst_1
6: ireturn
7: aload_1
8: ifnonnull 13
11: iconst_0
12: ireturn
13: aload_0
14: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class;
17: aload_1
18: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class;
21: if_acmpeq 26
24: iconst_0
25: ireturn
26: iconst_1
27: ireturn
public boolean eq2(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: if_acmpne 7
5: iconst_1
6: ireturn
7: aload_1
8: ifnonnull 13
11: iconst_0
12: ireturn
13: aload_0
14: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class;
17: aload_1
18: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class;
21: if_acmpeq 26
24: iconst_0
25: ireturn
26: iconst_1
27: ireturn
Es decir, recomendaría usar la primera versión (sin el else
). Algunas personas pueden argumentar que es más limpio con el otro partes, pero yo diría lo contrario. Incluyendo el else
indica que el programador no se dio cuenta de que era innecesario.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-14 13:00:36
No veo ninguna razón práctica para reemplazar una de esas implementaciones con la otra - en cualquier dirección.
El segundo ejemplo tendría sentido si se desea evitar múltiples declaraciones de retorno en un método - algunas personas prefieren esa forma de codificación. Entonces necesitamos las construcciones if-else if:
public boolean equals(Object obj)
{
boolean result = true;
if (this == obj)
result = true;
else if (obj == null)
result = false;
else if (getClass() != obj.getClass())
result = false;
return result;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-14 12:54:29
Piénsalo de esta manera. Cuando se ejecuta una sentencia return, control deja el método, por lo que else
no agrega ningún valor, a menos que quiera argumentar que agrega legibilidad (lo que realmente no creo que lo haga, pero otros pueden estar en desacuerdo).
Así que cuando tienes:
if (someCondition)
return 42;
if (anotherCondition)
return 43;
No hay realmente ningún valor en agregar un else
al segundo if
.
De hecho, uso una herramienta al escribir código C# llamada Resharper , y en realidad marcará el else
como inútil código en estas situaciones. Así que creo que, en general, es mejor dejarlos fuera. Y como ya mencionó Joachim, el compilador los optimiza de todos modos.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-14 12:53:31
Creo que este código se puede mejorar un poco (eso sí, es muy legible):
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
El operador instanceof
es equivalente a ambos combinados y probablemente es código menos rápido, y no hay invocaciones de método:
if (!(obj instanceof MyClass))
return false;
Pero qué sé yo.... Soy demasiado perezoso para analizar el código de bytes (nunca lo había hecho antes). :- p
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-21 07:09:30