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.