Prefacio
Todo el código de este artículo está escrito en JavaScript, pero también puede usar otros lenguajes de secuencias de comandos compatibles con JSR 223. Estos ejemplos se pueden ejecutar como archivos de script o en shells interactivos ejecutando una declaración a la vez. La sintaxis para acceder a propiedades y métodos de objetos en JavaScript es la misma que la del lenguaje Java.
Este artículo contiene las siguientes partes:
1. Acceder a las clases de Java
Para acceder a tipos nativos o tipos de Java de referencia en JavaScript, puede llamar a la función Java.type() , que devuelve el tipo de objeto correspondiente basado en el nombre de clase completa. El siguiente código muestra cómo obtener diferentes tipos de objetos:
var arrayList = java.type ("java.util.arrayList"); var inttype = java.type ("int"); var stringArrayType = java.type ("java.lang.string []"); var int2DarrayType = java.type ("int [] []"); El método para devolver un objeto tipo usando Java.type() en JavaScript es similar al de Java.
Por ejemplo, puede instanciar una clase utilizando el siguiente método:
var anarrayList = new Java.Type ("java.util.arrayList");Los objetos tipo Java se pueden usar para instanciar objetos Java. El siguiente código muestra cómo instanciar un nuevo objeto utilizando el constructor predeterminado y llame al constructor que contiene los parámetros:
var arrerayList = java.type ("java.util.arrayList"); var defaultSizeArrayList = new ArrayList; var costSizizearRayList = new ArrayList (16); Puede usar Java.type() para obtener el tipo de objeto, y puede usar los siguientes métodos para acceder a propiedades y métodos estáticos:
var file = java.type ("java.io.file"); file.createTempfile ("nashorn", ".tmp"); Si desea acceder a la clase estática interna, puede pasar el signo de dólar $ al método Java.type() .
El siguiente código muestra cómo devolver Float de java.awt.geom.Arc2D :
var float = java.type ("java.awt.geom.arc2d $ float");Si ya tiene un objeto de tipo de clase externa, puede acceder a su clase interna al igual que accedería a la propiedad, como se muestra a continuación:
var arc2d = java.type ("java.awt.geom.arc2d") var float = arc2d.floatDado que es una clase interna no estática, la instancia de clase externa debe pasar como un parámetro para el constructor.
Aunque usar objetos tipo en JavaScript es similar al de Java, todavía es algo diferente del objeto java.lang.Class . Esta diferencia es el valor de retorno del método getClass() . Puede usar class y las propiedades static para obtener esta información.
El siguiente código muestra la diferencia entre los dos:
var arrayList = java.type ("java.util.arrayList"); var a = new ArrayList; // Todos los siguientes son verdaderos: imprimir ("Tipo de tipo actúa como Target of instanceOf:" + (una instancia de ArrayList)); imprime ("La clase" no actúa como objetivo de instancia de instancia de: " +! (una instancia de a.getClass ())); print (type (" type no es el tipo de instancia (") + (a.getClass ()! == ArrayList)); imprimir ("La propiedad de la clase" de tipo es la misma que la instancia getClass (): " + (a.getClass () === ArrayList.Class)); imprime (" El tipo es la misma que la propiedad 'static` de la instancia getClass (): " + (A.getClassSss (). static === shatic);Sintaxis y semántica, expresiones de clase JavaScript y objetos de tiempo de ejecución son similares a la semántica de Java. Sin embargo, en Java, el objeto de clase no tiene una propiedad llamada Static porque la expresión de clase compilada no se usa como un objeto.
2. Importar paquetes y clases de Java
Para acceder a las clases de Java en función de su nombre simple, podemos usar importPackage() y importClass() para importar paquetes y clases de Java. Estas funciones existen en el archivo de script de compatibilidad (mozilla_compat.js).
El siguiente ejemplo muestra cómo usar importPackage() y importClass() :
// cargar compatibilidad scriptload ("nashorn: mozilla_compat.js"); // Importa el paquete java.awt PAGATIMPORTPACE (java.awt); // Importar el java.awt.frame classimportClass (java.awt.frame setVisible () MethodFrame.SetVisible (true); // Acceda a un Javabean PropertyPrint (Frame.title); Se puede acceder a los paquetes Java a través de la variable global de los paquetes, como Packages.java.util.Vector o Packages.javax.swing.JFrame . Sin embargo, el paquete Java SE estándar tiene métodos de acceso más simples, como: Java corresponde a Packages.java, Javax corresponde a Packages.javax y Org corresponde a Packages.org.
El paquete Java.Lang no requiere importación de forma predeterminada, porque esto entrará en conflicto con otros objetos incorporados de JavaScript como Object , Boolean y Math . Además, la importación de cualquier paquete y clases de Java también puede causar conflictos de nombres variables bajo el alcance global de JavaScript. Para evitar conflictos, definimos un objeto Javaimporter y limitamos el alcance de los paquetes y clases de Java importados a través de with declaración, como se muestra en el siguiente código:
// Cree un objeto Javaimporter con paquetes y clases especificados para importvar Gui = new Javaimporter (java.awt, javax.swing); // pase el objeto Javaimporter a la declaración "con" y accede a los clases // de los paquetes importados por sus simples nombres dentro de la declaración de la declaración (GUI) {var awtframe = new Frame (nuevo marco "); var jframe = new Jframe ("Swing JFrame");};3. Use matrices Java
Para crear un objeto de matriz Java, primero debe obtener el objeto de tipo de matriz Java e inicializarlo. El atributo de sintaxis y length del acceso de JavaScript a los elementos de matriz es lo mismo que Java, como se muestra en el siguiente código:
var stringArray = java.type ("java.lang.string []"); var a = new StringArray (5); // Establezca el valor del primer elemento [0] = "Scripting es grande!"; // Imprima la longitud de la ArrayPrint (a.lenggth); // imprima el valor del primer elemento (a [0]); Dada una matriz de JavaScript, también podemos convertirla en una matriz Java usando Java.to() . Necesitamos pasar la matriz de JavaScript como parámetro al método y especificar el tipo de matriz que se devolverá, que puede ser una cadena o un objeto tipo. También podemos ignorar los parámetros de objeto tipo para devolver la matriz de objeto []. La operación de conversión se realiza de acuerdo con las reglas de conversión de ECMAScript. El siguiente código muestra cómo convertir una matriz de JavaScript en una matriz de Java a través de diferentes parámetros Java.to() :
// Crear una matriz JavaScript var anArray = [1, "13", falso]; // Convertir una matriz a Java int [] Array var javaincarhArray = java.to (anArray, "int []"); imprimir (javaincarhArray [0]); // Imprime el número 1 de huella (JavaintArray [1]); // Imprime el número 13print (JavaintArray [2]); // Imprime el número 0 // Convertir la matriz de JavaScript a javastringararray = java.to (anArray, java.type ("java.lang.string []")); imprimir (javastringarray [0]); // Imprime la cadena "1" impresión (JavastringarArray [1]); // imprime la cadena "13" impresión (JavastringarArray [2]); // Imprime la cadena "False" // Convertir la matriz de JavaScript en objeto Java [] Array var javaObjectArray = java.to (anArray); print (javaObjectArray [0]); // Imprime el número 1 de huella (javaObjectArray [1]); // Imprime la cadena "13" impresión (javaObjectArray [2]); // imprime el valor booleano "falso" Puede usar Java.from() para convertir una matriz Java en una matriz JavaScript.
El siguiente código muestra cómo convertir una matriz que contiene la lista de archivos en el directorio actual en una matriz de JavaScript:
// Obtenga el archivo de tipo de archivo Java ObjectVar File = java.type ("java.io.file"); // Cree una matriz Java de archivo ObjectSVar listCurDir = new File ("."). ListFiles (); // Convertir la matriz de Java a un JavaScript ARRAYVAR JSLIST = java.from (listCurDir); ArrayPrint (jslist);Aviso:
En la mayoría de los casos, puede usar objetos Java en su script sin convertirlos en objetos JavaScript.
4. Implementar la interfaz Java
La sintaxis de implementar interfaces Java en JavaScript es similar al método de definir clases anónimas en Java. Solo necesitamos instanciar la interfaz e implementar sus métodos con las funciones de JavaScript.
El siguiente código demuestra cómo implementar la interfaz Runnable :
// Cree un objeto que implementa la interfaz ejecutable implementando // el método run () como un javaScript functionVar r = new java.lang.runnable () {run: function () {print ("running .../n"); }}; // La variable R se puede pasar a los métodos Java que esperan un objeto que implementa // el java.lang.runnable interfaceVar th = new java.lang.thread (r); th.start (); th.Join (); Si un método quiere un objeto, este objeto implementa una interfaz con solo un método, puede pasar una función de script a este método en lugar de pasar el objeto. Por ejemplo, en el ejemplo anterior, Thread() requiere un objeto que implementa Runnable como un parámetro. Podemos aprovechar la conversión automática para pasar una función de script al constructor Thread() .
El siguiente ejemplo muestra cómo crear un objeto Thread sin implementar una interfaz Runnable :
// Defina una función de FunctionFunction JavaScript func () {print ("¡Soy func!");}; // Pase la función JavaScript en lugar de un objeto que implementa // el java.lang.runnable interfaceVar th = new java.lang.thread (func); th.start (); th.join ();; Puede implementar múltiples interfaces pasando objetos de tipo relacionados a Java.extend() .
5. Extender las clases de Java abstractas
Puede instanciar una subclase de clase abstracta anónima, simplemente pasar un objeto JavaScript al constructor, que contiene algunas propiedades correspondientes a los valores implementados por el método de clase abstracta. Si se sobrecarga un método, la función JavaScript proporcionará implementaciones de todas las variantes de método. El siguiente ejemplo muestra cómo inicializar una subclase de la Clase Resumen TimeTask:
var timeTask = java.type ("java.util.timerTask"); var tarea = new TimeTask ({run: function () {print ("¡Hola mundo!")}}); Además de llamar al constructor y los parámetros de aprobación, también podemos proporcionar parámetros directamente después de la new expresión.
El siguiente ejemplo muestra cómo usar esta sintaxis (similar a la definición de clases internas anónimas en Java), que es un poco más simple que el ejemplo anterior:
var tarea = new TimeTask {run: function () {print ("¡Hola mundo!")}};Si la clase abstracta contiene un solo método abstracto (tipo SAM), entonces no necesitamos pasar un objeto JavaScript al constructor, podemos pasar una interfaz de función que implementa el método. El siguiente ejemplo muestra cómo usar tipos SAM para simplificar el código:
var tarea = new TimeTask (function () {print ("¡Hola mundo!")});Independientemente de la sintaxis que elija, si necesita llamar a un constructor que contenga parámetros, puede especificar parámetros en el objeto y función de implementación.
Si desea llamar a un método Java que requiere parámetros de tipo SAM, puede pasar una función JavaScript al método. Nashorn instanciará una subclase de acuerdo con las necesidades del método y usará esta función para implementar el método abstracto único.
El siguiente código muestra cómo llamar Timer.schedule() , que requiere un objeto TimerTask como parámetro:
VAR TIMER = java.type ("java.util.timer"); timer.schedule (function () {print ("¡Hola mundo!")});Aviso:
La sintaxis anterior supone que el tipo SAM requerido es una interfaz o contiene un constructor predeterminado, que Nashorn usa para inicializar una subclase. Esto no es posible usar una clase que no contenga el constructor predeterminado.
6. extender clases de Java específicas
Para evitar confusiones, la sintaxis para extender las clases abstractas no se puede usar para extender las clases concretas. Debido a que una clase concreta se puede instanciar, dicha sintaxis se analiza en un intento de crear una nueva instancia de clase y pasar el objeto de la clase requerida por el constructor (si el tipo de objeto esperado es una interfaz). Para demostrar este problema, eche un vistazo al siguiente código de muestra:
var t = new java.lang.thread ({run: function () {print ("hilo en ejecución!")}}); Esta línea de código se analiza para extender la clase Thread e implementar run() , y la instancia de Thread se pasa a su constructor un objeto que implementa la interfaz ejecutable.
Para extender una clase concreta, pase su objeto de tipo a la función Java.extend() y luego devuelva su objeto de tipo a su subclase. Luego puede usar el objeto Tipo de esta subclase para crear instancias y proporcionar implementaciones de métodos adicionales.
El siguiente código le mostrará cómo extender Thread e implementar run() :
var thread = java.type ("java.lang.thread"); var ThreadExtender = java.extend (hilo); var t = new ThreadExtender () {run: function () {print ("hilo en ejecución!")}}; Java.extend() puede obtener una lista de múltiples tipos de objetos. No puede especificar más de un objeto de tipo Java, o puede especificar el número de objetos de tipo tantos como interfaces Java. El objeto de tipo devuelto extiende la clase especificada (o java.lang.Object , si no hay objeto de tipo especificado), esta clase implementa todas las interfaces. Los objetos tipo de la clase no necesitan estar en la parte superior de la lista.
7. Métodos para acceder a la superclase (clase principal)
Los métodos que desean acceder a la clase principal pueden usar la función Java .super() .
El siguiente ejemplo muestra cómo extender java.lang.Exception y acceder a los métodos de la clase principal.
Ejemplo 3-1 Método para acceder a la clase principal (super.js) var excepción = java.type ("java.lang.exception"); var excepcionAdapter = java.extend (excepción); var excepción = new ExceptionAdapter ("My Exception Mensaje") {getMessage: function () {var _super_ = java.super (excepción); return _super_.getMessage (). toUpperCase (); }} try {Throw Exception;} Catch (Ex) {Print (Exception);}Si ejecuta el código anterior, imprimirá lo siguiente:
jdk.nashorn.javaadapters.java.lang.exception: mi mensaje de excepción
8. Vidiente para implementar a clase
En la sección anterior describimos cómo extender las clases de Java e implementar la interfaz utilizando un parámetro de objeto JavaScript adicional. La implementación está vinculada en una instancia específica, que se crea a través de una nueva clase. Hay algunos beneficios al hacer esto, como la huella de memoria en tiempo de ejecución, ya que Nashorn puede crear un solo adaptador universal para la combinación de tipos de cada implementación.
El siguiente ejemplo muestra que diferentes instancias pueden ser la misma clase Java, pero sus objetos de implementación de JavaScript son diferentes:
var runnable = java.lang.runnable; var r1 = new runnable (function () {print ("I'm runnable 1!")}); var r2 = new runnable (function () {print ("I'm runnable 2!")}); r1.run (); r2.run (); imprime ("We Comparte la misma clase:" + ((r1.class);El código anterior imprimirá el siguiente resultado:
¡Estoy ejecutable 1! ¡Soy Runnable 2! Compartimos la misma clase: Verdadero
Si desea pasar una instancia de una clase a una API externa (como el marco Javafx, transmitiendo una instancia de aplicación a la API Javafx), debe extender una clase Java o implementar una interfaz vinculada a esa clase, en lugar de su instancia. Puede implementar la clase pasando una unión de objeto JavaScript y pasando al último parámetro de la función Java.extend (). Esto crea una nueva clase con el mismo constructor que la clase original, porque no requieren una implementación adicional de los parámetros de objetos.
El siguiente ejemplo muestra cómo vincular una implementación en una clase y demuestra que las clases de implementación son diferentes para diferentes llamadas en este caso:
var runnableImpl1 = java.extend (java.lang.runnable, function () {imprime ("I'm runnable 1!")}); var runnableImpl2 = java.extend (java.lang.runnable, function () {imprime ("I'm runnable 2!")}); var r1 = new runnableMpL1 (); new = nie RunnableImpl2 (); r1.run (); r2.run (); imprime ("Compartimos la misma clase:" + (r1.class === r2.class));Los resultados de ejecución de ejemplo anteriores son los siguientes:
¡Estoy ejecutable 1! Estoy ejecutable 2! Compartimos la misma clase: Falso
Mover el objeto de implementación desde la llamada del constructor a Java.extend() Función Evita los parámetros adicionales requeridos en la llamada del constructor. Cada llamada a Java.extend() requiere un objeto de implementación de la clase especificada para generar una nueva clase de adaptador Java. Las clases de adaptador implementadas con límites de clase aún pueden usar un parámetro de constructor adicional para anular aún más el comportamiento de una instancia específica. Por lo tanto, puede fusionar estos dos métodos: puede proporcionar parte de la implementación de JavaScript en una clase base, luego pasarla a la función Java.extend() y proporcionar una implementación de instancia en el objeto y pasarlo al constructor. Algunas definiciones de funciones del objeto se sobrescribirán cuando el objeto se define y pase al constructor.
El siguiente código demuestra cómo sobrescribir una función de un objeto de límite de clase pasando una función al constructor:
var runnableImpl = java.extend (java.lang.runnable, function () {imprime ("I'm runnable 1!")}); var r1 = new runnableImpl (); var r2 = new runnableImpl (function () {imprime ("I'm runnable 2!")}); r1.run (); r2.run (); imprime (imprime ("SE RUMNABLE 2!")}); r1.run (); r2.run (); imprime ("SE CLASS:" SE RUMNA (r1.class === r2.class));Los resultados de la impresión después del ejemplo anterior se ejecuta son los siguientes:
¡Estoy ejecutable 1! ¡Soy Runnable 2! Compartimos la misma clase: Verdadero
9. Seleccione la variante de sobrecarga del método
Los métodos Java se pueden sobrecargar utilizando diferentes tipos de parámetros. El compilador Java (JAVAC) seleccionará el método correcto para ejecutar en el momento de la compilación. El análisis de los métodos sobrecargados de Java en Nashorn se ejecuta cuando se llama al método. También es una forma de determinar el método correcto basado en el tipo de parámetro. Pero si el tipo de parámetro real causa ambigüedad, podemos especificar explícitamente una variante sobrecargada específica. Esto mejora el rendimiento de la ejecución del programa, porque el motor Nashorn no necesita distinguir qué método llamar durante la llamada.
Las variantes sobrecargadas están expuestas como propiedades especiales. Podemos referirnos a ellas en forma de cadenas, que contienen nombres de métodos y tipos de parámetros, y están rodeados de paréntesis.
El siguiente ejemplo muestra cómo llamar System.out.println() con Object , le pasamos una cadena "Hola":
var out = java.lang.system.out; out ["println (object)"] ("hola");En el ejemplo anterior, usar solo el nombre de la clase de objeto es suficiente porque es la firma que identifica de manera única la correcta. El caso en el que debe usar el nombre de clase completa es que dos funciones de variantes sobrecargadas usan diferentes tipos de parámetros, pero el tipo tiene el mismo nombre (esto es posible, por ejemplo, diferentes paquetes contienen el mismo nombre de clase).
10. Mapeo de tipos de datos
La gran mayoría de las conversiones anteriores de Java y JavaScript funcionan bien como esperaría. En los capítulos anteriores, mencionamos algunas asignaciones de tipo de datos simples entre Java y JavaScript. Por ejemplo, los datos de tipo de matriz se pueden convertir explícitamente, las funciones de JavaScript se pueden convertir automáticamente a los tipos SAM cuando se pasan como parámetros a los métodos Java. Cada objeto JavaScript implementa la interfaz java.util.Map para permitir que la API acepte mapeos directamente. Al pasar los valores a la API de Java, se convertirán al tipo numérico objetivo esperado, que puede ser un tipo de encapsulación o un tipo de datos primitivos. Si el tipo de destino no es muy cierto (como el número), solo puede requerir que sea el tipo de número, y luego encapsule específicamente el tipo, como doble, entero, largo, etc. La optimización interna genera el valor numérico de cualquier tipo de paquete. Colegas, puede pasar cualquier valor de JavaScript a la API Java, ya sea un tipo encapsulado o un tipo primitivo, porque el algoritmo de conversión ToNumber de JavaScript procesará automáticamente su valor. Si un método Java requiere una String o parámetro de objeto Boolean , JavaScript utilizará transformaciones ToString y ToBoolean para obtener su valor.
Aviso:
Debido a las consideraciones internas de optimización del rendimiento para las operaciones de cadena, las cadenas JavaScript no siempre corresponden al tipo Java.Lang.String, o también pueden ser java.lang.CharSequence . Si pasa una cadena JavaScript a un método Java que requiere el parámetro java.lang.String , entonces la cadena JavaScript es el tipo java.lang.String , pero si su firma de método quiere ser más genérica (por ejemplo, el tipo de parámetro aceptado es Java.lang.object), luego el objeto de parámetro que obtendrá un objeto que implementa CharSequence , no es un objeto de Java Java.lang.
Resumir
Lo anterior es todo el contenido de este artículo. Espero que sea de ayuda para el estudio y el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse.