Dada la siguiente clase usando Java 8 Optional :
final class Main { public static void main(final String[] args) { System.out.println(Optional.of("test").get()); } }Si compilo el código con un compilador de Java 8 dirigido al código de bytes de Java 7:
javac -target 1.7 -source 1.7 Main.java Cuando ejecuto con una JVM de Java 7, main arroja un NoClassDefFoundError envuelve una ClassNotFoundException para java.util.Optional como se esperaba.
Sin embargo, si verifico la disponibilidad de la clase Optional (a través de la reflexión) antes de usarla:
final class Main { public static void main(final String[] args) { if (isOptionalAvailable()) { System.out.println(Optional.of("test").get()); } else { System.out.println("Optional not found."); } } private static boolean isOptionalAvailable() { try { Class.forName("java.util.Optional"); return true; } catch (ClassNotFoundException e) { return false; } } }Cuando ejecuto con una JVM de Java 7, no arroja ningún error:
Optional not found.Lo que estoy tratando de averiguar es si las especificaciones JVM o JLS requieren este comportamiento. Parece que el comportamiento es consistente en Oracle, IBM y OpenJDK, pero no pude encontrar ningún requisito en las especificaciones de que las clases que se usan localmente en los métodos deben cargarse con pereza.
Revisé el"Capítulo 5. Carga, vinculación e inicialización" de la especificación JVM y "15.12.4. Evaluación en tiempo de ejecución de la invocación del método" en el JLS.
Para mi segundo ejemplo, ¿podría haber un impl de JVM que cargue Optional con entusiasmo aunque solo exista en una ruta de código no utilizada? ¿Me estoy perdiendo la sección de las especificaciones que requiere este comportamiento o es solo un detalle de implementación común?
No hay garantía de que la clase no se cargue.
ConsidereJLS, §5.4 :
Esta especificación permite una flexibilidad de implementación en cuanto a cuándo tienen lugar las actividades de vinculación (y, debido a la recursividad, la carga), siempre que se mantengan todas las propiedades siguientes:
…
Por ejemplo, una implementación de Java Virtual Machine puede elegir una estrategia de vinculación "perezosa", donde cada referencia simbólica en una clase o interfaz (aparte de las referencias simbólicas anteriores) se resuelve individualmente cuando se usa. Alternativamente, una implementación puede elegir una estrategia de vinculación "ansiosa", donde todas las referencias simbólicas se resuelven a la vez cuando se verifica la clase o la interfaz.
Incluso HotSpot JVM, que utiliza la carga diferida de clases, puede intentar cargar la clase antes de lo esperado, es decir, fuera de esa ruta de código no utilizada, debido a aspectos sutiles del código, que pueden requerir que el verificador cargue una clase, como se explica en Cuándo ¿Está cargada una clase de Java?
En otras palabras, incluso con esta implementación de JVM, pequeños cambios en el código pueden hacer que falle repentinamente cuando la clase está ausente.