После нашей предыдущей статьи о том, как использовать jOOQ с Java 8 и Nashorn , один из наших пользователей обнаружил недостаток в использовании jOOQ API, как обсуждалось здесь в группе пользователей . В сущности, недостаток можно резюмировать так:
Java-код
01
02
03
04
05
06
07
08
09
10
11
|
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
1
2
|
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, а не тех, которые интуитивно кажутся «точными» совпадениями с методами varargs!
Пусть это утонет!
Учитывая, что теперь мы знаем, как Nashorn разрешает перегрузку, мы можем видеть, что любое из следующего является допустимым обходным путем:
Явный вызов метода test (Integer []) с использованием аргумента массива:
Это самый простой подход, когда вы игнорируете тот факт, что varargs существуют, и просто создаете явный массив:
1
2
|
var API = Java.type( "org.jooq.nashorn.test.API" ); API.test([ 1 ]); |
Явный вызов метода test (Integer []) следующим образом:
Это, безусловно, самый безопасный подход, так как вы удаляете всю неоднозначность из вызова метода:
1
2
|
var API = Java.type( "org.jooq.nashorn.test.API" ); API[ "test(Integer[])" ]( 1 ); |
Снятие перегрузки:
1
2
3
4
5
|
public class AlternativeAPI1 { public static void test(Integer... args) { System.out.println( "OK" ); } } |
Удаление varargs:
1
2
3
4
5
6
7
8
9
|
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" ); } } |
Предоставление точного варианта:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
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 (или любой другой «подобный тип»):
Теперь это интересно:
1
2
3
4
5
6
7
8
9
|
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, язык, который говорит вам во время выполнения, что:
1
|
[ '10' , '10' , '10' , '10' ].map(parseInt) |
… дает
1
|
[ 10 , NaN, 2 , 3 ] |
… и где
1
|
++[[]][+[]]+[+[]] === "10" |
дает истину! ( источники здесь )
Для получения дополнительной информации о JavaScript, пожалуйста, посетите этот вводный урок .
Ссылка: | Как Nashorn влияет на развитие API на новом уровне от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ . |