varargs java javascript java-8 nashorn

java - Error de Nashorn al llamar al método sobrecargado con el parámetro varargs



java parameters command line (3)

Como el tipo que escribió el mecanismo de resolución de sobrecarga para Nashorn, siempre me fascinan los casos de esquina en los que se topa la gente. Para bien o para mal, así es como esto termina siendo invocado:

La resolución del método de sobrecarga de Nashorn imita al máximo la especificación del lenguaje Java (JLS), pero también permite conversiones específicas de JavaScript. JLS dice que cuando se selecciona un método para invocar un nombre sobrecargado, los métodos de aridad variable pueden considerarse para invocación solo cuando no hay un método de aridad fija aplicable. Normalmente, cuando se invoca desde la test(String) Java test(String) no sería aplicable a una invocación con un int , por lo que se test(Integer...) método de test(Integer...) . Sin embargo, dado que JavaScript realmente permite la conversión implícita de número a cadena, es aplicable y se considera antes que cualquier método de aridad variable. De ahí el comportamiento observado. Arity triunfa sobre la no conversión. Si agregó un método de test(int) , se invocará antes del método de cadena, ya que es una aridad fija y es más específico que la de la cadena.

Podría argumentar que deberíamos alterar el algoritmo para elegir el método. Se ha pensado mucho en esto desde antes incluso del proyecto Nashorn (incluso cuando estaba desarrollando Dynalink de forma independiente). El código actual (tal como está incorporado en la biblioteca Dynalink, sobre el que Nashorn se basa en realidad) sigue a JLS hasta la letra y, en ausencia de conversiones de tipo específicas del idioma, elegirá los mismos métodos que Java. Sin embargo, tan pronto como comienza a relajar su sistema de tipos, las cosas comienzan a cambiar sutilmente, y cuanto más lo relaja, más cambiarán (y JavaScript se relaja mucho ), y cualquier cambio en el algoritmo de elección tendrá algún otro extraño comportamiento con el que alguien más se encontrará ... solo que viene con el sistema de tipo relajado, me temo. Por ejemplo:

  • Si permitimos que varargs se considere junto con fixargs, tendríamos que inventar una relación "más específica que" entre los diferentes métodos de aridad, algo que no existe en JLS y, por lo tanto, no es compatible con él, y causaría varargs. a veces se invoca cuando, de lo contrario, JLS prescribiría la invocación de fixargs.
  • Si rechazamos las conversiones permitidas por JS (lo que obliga a que la test(String) no se considere aplicable a un parámetro int ), algunos desarrolladores de JS se sentirían gravados por la necesidad de contorsionar su programa para invocar el método String (por ejemplo, haciendo una test(String(x)) para asegurar que x sea ​​una cadena, etc.

Como puedes ver, no importa lo que hagamos, algo más sufriría; la selección de métodos sobrecargados se encuentra en un lugar estrecho entre los sistemas de tipo Java y JS y es muy sensible incluso a pequeños cambios en la lógica.

Finalmente, cuando selecciona manualmente las sobrecargas, también puede atenerse a nombres de tipo no calificados, siempre que no haya ambigüedad en las posibles firmas de métodos para el nombre del paquete en la posición del argumento, es decir

API["test(Integer[])"](1);

debería funcionar también, no es necesario para el java.lang. prefijo. Eso podría aliviar un poco el ruido sintáctico, a menos que pueda volver a trabajar la API.

HTH, Atila.

Supongamos la siguiente API:

package nashorn.test; public class API { public static void test(String string) { throw new RuntimeException("Don''t call this"); } public static void test(Integer... args) { System.out.println("OK"); } }

El siguiente fragmento de código de JavaScript de Nashorn fallará:

var API = Java.type("nashorn.test.API"); API.test(1);

El primer método será llamado en lugar del segundo. ¿Es esto un error en el motor de Nashorn?

Para el registro, este problema se informó anteriormente en el grupo de usuarios jOOQ , donde se utilizan muchos métodos de sobrecarga y varargs, y donde este problema puede causar muchos problemas.

Sobre el boxeo

Puede haber una sospecha de que esto podría tener que ver con el boxeo. No lo hace El problema también aparece cuando lo hago.

public class API { public static void test(String string) { throw new RuntimeException("Don''t call this"); } public static void test(Integer... args) { System.out.println("OK"); } public static void test(MyType... args) { System.out.println("OK"); } }

Y:

public class MyType { }

Y entonces:

var API = Java.type("nashorn.test.API"); var MyType = Java.type("nashorn.test.MyType"); API.test(new MyType());


Esta es una situación ambigua. En el segundo caso, se busca una matriz de enteros o más de un entero para distinguirlos del primer caso. Puedes usar el método de selección para decirle a Nashorn a qué caso te refieres.

API["test(java.lang.Integer[])"](1);


Estas son soluciones válidas:

Llamar explícitamente al método de test(Integer[]) usando un argumento de matriz:

var API = Java.type("nashorn.test.API"); API.test([1]);

Eliminando la sobrecarga:

public class AlternativeAPI1 { public static void test(Integer... args) { System.out.println("OK"); } }

Eliminando los varargs:

public class AlternativeAPI3 { public static void test(String string) { throw new RuntimeException("Don''t call this"); } public static void test(Integer args) { System.out.println("OK"); } }

Sustitución de String por CharSequence (o cualquier otro "tipo similar"):

public class AlternativeAPI2 { public static void test(CharSequence string) { throw new RuntimeException("Don''t call this"); } public static void test(Integer args) { System.out.println("OK"); } }