código·java·oracle
Java y JVM

Paso de variables de Java a JavaScript: de LiveConnect a GraalJS

Veinte años de puentes entre dos lenguajes: JSObject, Rhino, Nashorn y el presente polyglota de GraalVM. La historia de una frontera que se movió cuatro veces.

publicado noviembre de 2011 revisado agosto de 2026 608 palabras

«¿Cómo paso esta variable de Java a JavaScript?» fue una pregunta real de 2011, con applets por medio, y su respuesta ha cambiado de dirección cuatro veces en veinte años: LiveConnect, Rhino, Nashorn, GraalVM. Ninguna tecnología de esta lista murió por no funcionar; murieron por evolución de la plataforma. Repasar esa frontera es repasar la historia reciente de la JVM, y las lecciones siguen vigentes para cualquier integración entre lenguajes.

Dos pantallas enfrentadas con espejos entre ellas reflejando líneas de código difusas en tonos naranja y azul
El puente entre lenguajes: dos pantallas, un reflejo, ¿cuál es el original?

La era de los applets: JSObject y LiveConnect

Cuando el código Java vivía dentro del navegador, la comunicación era de doble sentido: JavaScript llamaba métodos públicos del applet y el applet manipulaba la página mediante JSObject:

JAVALiveConnect.java
// dentro de un applet, anno 2011
JSObject ventana = JSObject.getWindow(this);
ventana.setMember("totalCarrito", 240);     // variable hacia JS
Object r = ventana.eval("validarCarrito()");
ventana.call("mostrarAviso", new Object[]{"Pago procesado"});

Ese era el «paso de variables» literal del título original. Los applets fueron retirados del JDK en 11 (2018) y del navegador mucho antes, pero el patrón de esa API —getMember, setMember, call, eval— sobrevive casi intacto en GraalVM: las fronteras entre lenguajes envejecen mejor que las tecnologías que las unen.

Nashorn: JavaScript como ciudadano del JDK

Java 8 (2014) trajo Nashorn, un motor JavaScript completo dentro del JDK. Pasar variables se volvió trivial con ScriptEngine:

JAVANashorn.java
import javax.script.*;

ScriptEngine motor = new ScriptEngineManager()
        .getEngineByName("nashorn");

motor.put("unidades", 3);
motor.put("precio", 79.90);

Object total = motor.eval("unidades * precio * 1.21");
System.out.println(total);   // 290.27...

// bindings aislados para cada ejecucion
Bindings b = motor.createBindings();
b.put("nombre", "Ada");
Object saludo = motor.eval(
    "var s = 'Hola, ' + nombre; s.toUpperCase()", b);

put publica la variable en el motor y eval la consume; los Bindings aislados evitan que ejecuciones concurrentes pisen las variables unas a otras —el equivalente exacto de un ámbito local. Nashorn fue deprecado en 11 y retirado en 15: el ritmo de ECMAScript superaba al de un motor mantenido dentro del JDK. La lección institucional: los lenguajes invitados exigen ritmo de liberación propio, algo que la JVM aprendió y resolvió de otra manera.

El presente: GraalVM y el poliglotismo

JAVAPolygloto.java
import org.graalvm.polyglot.*;

try (Context ctx = Context.create()) {
    Value bindings = ctx.getBindings("js");
    bindings.putMember("unidades", 3);
    bindings.putMember("precio", 79.90);

    Value total = ctx.eval("js",
        "(unidades * precio * 1.21).toFixed(2)");

    System.out.println(total.asString());   // "290.28"
}

En GraalVM, el Context delimita una máquina virtual invitada completa: dentro puede vivir JavaScript, Python, Ruby o WebAssembly, y las fronteras se cruzan con putMember/getMember —nombres heredados casi literalmente de JSObject. Value es el tipo universal de la otra orilla: se pregunta (isNumber, hasMembers) antes de convertir (asDouble, asString), porque el sistema de tipos del otro lado nunca es el tuyo.

Reglas de la frontera, válidas en todas las eras

  • Copia, no referencia: los valores cruzan como copias o como referencias opacas; asumir semántica compartida de objetos es la fuente número uno de bugs en cualquier puente.
  • Tipos en la frontera: números, cadenas y booleanos cruzan bien; fechas, colecciones y objetos complejos necesitan conversión explícita o mappings declarados.
  • Errores traducidos: una excepción del lado invitado llega como PolyglotException (o ScriptException en Nashorn): captura la envoltura y pregunta por la causa.
  • Cuándo no hacerlo: si solo necesitas calcular un importe, escribe el double en Java. El puente se justifica cuando el otro lado tiene un ecosistema que el tuyo no —librerías npm, reglas de negocio ya escritas, fórmulas del usuario.

Los puentes entre lenguajes se construyen con la mejor tecnología del día y se jubilan por la siguiente. Diseña la frontera para poder cambiar el puente sin cambiar las orillas.

De la misma época tecnológica quedan otras dos piezas visitables: la reflexión, que todos estos motores usaban por debajo para tocar objetos Java, y el juego de la serpiente, que vio nacer y morir el mundo de los applets en primera fila.