После нашей предыдущей статьи о том, как использовать jOOQ с Java 8 и Nashorn , один из наших пользователей обнаружил недостаток в использовании jOOQ API, как обсуждалось здесь в группе пользователей . В сущности, недостаток можно резюмировать так:
Java-код
package org.jooq.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"); } }
Код JavaScript
var API = Java.type("org.jooq.nashorn.test.API"); API.test(1); // This will fail with RuntimeException
После некоторого расследования и любезной помощи Аттилы Сегеди , а также Джима Ласки (обоих разработчиков Nashorn из Oracle) стало ясно, что Nashorn устраняет неоднозначность перегруженных методов и переменных по-разному, чем того мог ожидать старый Java-разработчик. Цитирую Аттилу:
Разрешение метода перегрузки Nashorn имитирует спецификацию языка Java (JLS) в максимально возможной степени, но допускает также специфичные для JavaScript преобразования. JLS говорит, что при выборе метода для вызова для перегруженного имени методы переменной арности могут рассматриваться для вызова только в том случае, если нет применимого метода фиксированной арности.
Я согласен с тем, что методы переменной арности можно рассматривать только тогда, когда нет применимого метода фиксированной арности. Но само понятие «применимы» себя полностью изменено , как продвижение типа (или принуждение / преобразование) с помощью ToString , ToNumber , ToBoolean является предпочтительным над тем, что интуитивно кажется, «точным» матчи с переменной длиной методы!
Пусть это утонет!
Учитывая, что теперь мы знаем, как Nashorn разрешает перегрузку, мы можем видеть, что любое из следующего является допустимым обходным путем:
Явный вызов метода test (Integer []) с использованием аргумента массива:
Это самый простой подход, когда вы игнорируете тот факт, что varargs существуют, и просто создаете явный массив.
var API = Java.type("org.jooq.nashorn.test.API"); API.test([1]);
Явный вызов метода test (Integer []) следующим образом:
Это, безусловно, самый безопасный подход, поскольку вы убираете всю неопределенность из вызова метода.
var API = Java.type("org.jooq.nashorn.test.API"); API["test(Integer[])"](1);
Снятие перегрузки:
public class AlternativeAPI1 { public static void test(Integer... args) { System.out.println("OK"); } }
Удаление 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"); } }
Предоставление точного варианта:
public class AlternativeAPI4 { public static void test(String string) { throw new RuntimeException("Don't call this"); } public static void test(Integer args) { test(new Integer[] { args }); } public static void test(Integer... args) { System.out.println("OK"); } }
Замена String на CharSequence (или любой другой «подобный тип»):
Теперь это интересно:
public class AlternativeAPI5 { public static void test(CharSequence string) { throw new RuntimeException("Don't call this"); } public static void test(Integer args) { System.out.println("OK"); } }
В частности, на мой взгляд , различие между типами CharSequence
и String
типами кажется очень случайным с точки зрения Java.
Согласитесь, реализовать преобразование перегруженного метода в динамически типизированном языке очень сложно, если вообще возможно. Любое решение — это компромисс, который может привести к ошибкам с некоторых сторон. Или, как сказал Аттила:
Как видите, что бы мы ни делали, пострадает что-то другое; Выбор перегруженного метода находится в трудном положении между системами типов Java и JS и очень чувствителен даже к небольшим изменениям в логике.
Правда! Но не только выбор метода перегрузки очень чувствителен даже к небольшим изменениям. Использование Nashorn с совместимостью с Java тоже! Как разработчик API, с годами я привык к семантическому версионированию и множеству тонких правил, которым нужно следовать, чтобы обеспечить совместимость с исходным кодом API, совместимость с поведением — и, если возможно, — в значительной степени с двоичной совместимостью .
Забудьте об этом, когда ваши клиенты используют Nashorn. Они сами по себе. Вновь введенная перегрузка в вашем Java API может очень сильно сломать ваших клиентов Nashorn. Но опять же, это JavaScript, язык, который говорит вам во время выполнения, что:
['10','10','10','10'].map(parseInt)
… дает
[10, NaN, 2, 3]
… и где
++[[]][+[]]+[+[]] === "10"
дает истину! ( источники здесь )
Для получения дополнительной информации о JavaScript, пожалуйста, посетите этот вводный урок .