ternario - if y switch en java
Operador ternario Java vs if/else en compatibilidad<JDK8 (4)
Recientemente estoy leyendo el código fuente de Spring Framework. Algo que no puedo entender va aquí:
public Member getMember() {
// NOTE: no ternary expression to retain JDK <8 compatibility even when using
// the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
// as common type, with that new base class not available on older JDKs)
if (this.method != null) {
return this.method;
}
else {
return this.constructor;
}
}
Este método es miembro de la clase
org.springframework.core.MethodParameter
.
El código es fácil de entender mientras que los comentarios son difíciles.
NOTA: ninguna expresión ternaria para retener la compatibilidad JDK <8 incluso cuando se utiliza el compilador JDK 8 (potencialmente seleccionando
java.lang.reflect.Executable
como tipo común, con esa nueva clase base no disponible en JDK anteriores)
¿Cuál es la diferencia entre usar expresiones ternarias y usar
if...else...
construct en este contexto?
Cuando piensa en el tipo de operandos, el problema se hace más evidente:
this.method != null ? this.method : this.constructor
tiene como tipo el tipo común más especializado de ambos operandos, es decir, el tipo más especializado común tanto para
this.method
como para
this.constructor
.
En Java 7 esto es
java.lang.reflect.Member
, sin embargo, la biblioteca de clases Java 8 introduce un nuevo tipo
java.lang.reflect.Executable
que es más especializado que el
Member
genérico.
Por lo tanto, con una biblioteca de clases Java 8, el tipo de resultado de la expresión ternaria es
Executable
lugar de
Member
.
Algunas versiones (anteriores al lanzamiento) del compilador Java 8 parecen haber producido una referencia explícita a
Executable
dentro del código generado al compilar el operador ternario.
Esto desencadenaría una carga de clase y, a su vez, una
ClassNotFoundException
en tiempo de ejecución cuando se ejecuta con una biblioteca de clases <JDK 8, porque el
Executable
solo existe para JDK ≥ 8.
Como señaló Tagir Valeev en esta respuesta , esto es realmente un error en las versiones preliminares de JDK 8 y desde entonces se ha solucionado, por lo que tanto la solución alternativa como el comentario explicativo ahora son obsoletos.
Nota adicional: Se podría llegar a la conclusión de que este error del compilador estaba presente antes de Java 8. Sin embargo, el código de bytes generado para el ternario por OpenJDK 7 es el mismo que el código de bytes generado por OpenJDK 8. De hecho, el tipo de la expresión no se menciona por completo en tiempo de ejecución, el código es realmente solo prueba, ramificación, carga, devolución sin realizar ninguna comprobación adicional. Así que tenga la seguridad de que esto ya no es un problema (y de hecho) parece haber sido un problema temporal durante el desarrollo de Java 8.
El tipo de valor de retorno en una expresión ternaria se ve afectado por las clases primarias, que cambiaron como se describe en Java 8.
Es difícil ver por qué no se pudo haber escrito un elenco.
Esto se introdujo en
una confirmación bastante antigua
el 3 de mayo de 2013, casi un año antes del lanzamiento oficial de JDK-8.
El compilador estaba bajo un fuerte desarrollo en esos momentos, por lo que podrían surgir problemas de compatibilidad.
Supongo que el equipo de Spring acaba de probar la compilación JDK-8 e intentó solucionar problemas, aunque en realidad son problemas de compilación.
Por el lanzamiento oficial de JDK-8 esto se volvió irrelevante.
Ahora el operador ternario en este código funciona bien como se esperaba (no hay referencia a la clase
Executable
en el archivo compilado .class).
Actualmente aparecen cosas similares en JDK-9: algunos códigos que pueden compilarse bien en JDK-8 fallan con JDK-9 javac. Supongo que la mayoría de estos problemas se solucionarán hasta el lanzamiento.
La principal diferencia es que un bloque
if
else
es una
declaración,
mientras que el ternario (más conocido como operador
condicional
en Java) es una
expresión
.
Una
declaración
puede hacer cosas como
return
a la persona que llama en algunas de las rutas de control.
Se puede usar una
expresión
en una tarea:
int n = condition ? 3 : 2;
Por lo tanto, las dos expresiones en el ternario después de la condición deben ser en mayúsculas al mismo tipo.
Esto puede causar algunos efectos extraños en Java, particularmente con el auto-boxeo y la transmisión automática de referencias: esto es a lo que se refiere el comentario en su código publicado.
La coerción de las expresiones en su caso sería un tipo
java.lang.reflect.Executable
(ya que es el
tipo más especializado
) y que no existe en versiones anteriores de Java.
Estilísticamente, debe usar un bloque
if
else
si el código tiene forma de declaración y un ternario si tiene forma de expresión.
Por supuesto, puede hacer que un bloque
if
else
se comporte como una expresión si usa una función lambda.