Как стать автором
Обновить

the Da Vinci Machine Project в Java 7 и борьба с велосипедами

Время на прочтение 4 мин
Количество просмотров 1.4K
   Добрый день, уважаемые хабражители!

   Совсем недавно вышла ожидаемая многими Java 7. К сожалению, многих разочаровал состав нововведий, так как в него не попали различные очень ожидаемые вкусности вроде Project Lambda. Однако нововведений всё же много и сегодня я хотел бы немного остановиться на одном из важнейших — the Da Vinci Machine Project, который позволяет пользоваться динамическими языками на JVM более эффективно. Если говорит точнее, то рассматривать мы будем одну из частей the Da Vinci Machine Project — method handle. До конца проникнуться концепциями этой части языка я ещё не успел, но большинство людей вообще не понимают, зачем оно надо :) В статье я рассмотрю один use case, который Java-программистам знаком не по наслышке и родил, думаю, наибольшее число велосипедов ever. Он, конечно, касается перегрузки методов и передачи параметров по интерфейсу.

   Известно, что в Java решение о том, какой из перегруженных методов вызывать производится на этапе компиляции (в отличии, скажем, от C#, где это делается во время исполнения). Предположим, у нас есть следующий код:
  1. class A {
  2. }
  3.  
  4. class B extends A {
  5. }
  6.  
  7. public class Test {
  8.   
  9.   public void call(A a) {
  10.     System.out.println("A");
  11.   }
  12.   
  13.   public void call(B b) {
  14.     System.out.println("B");
  15.   }
  16.   
  17.   public static void main(String[] argv) {
  18.     A b = new B();
  19.     Test test = new Test();
  20.     test.call(b);
  21.   }
  22. }
* This source code was highlighted with Source Code Highlighter.

   По правилам Java будет вызван метод public void call(A a), и соответственно выведено на экран «A», что может показаться странным, потому что реальный тип объекта B. Для того, чтобы вызвать нужный нам метод необходимо переписать код вызова метода так:
  1. if (b instanceof B) {
  2.   test.call((B) b);
  3. }
* This source code was highlighted with Source Code Highlighter.


   Данный подход вполне имеет право на жизнь, но если у вас несколько десятков таких классов, то код становится банально уродливым, появляется дублирование и вообще.

   В Java 7 появились средства, которые могут помочь решить данную проблему, если не на уровне языка, то хотя бы не уровне костылей. Итак, помогут нам классы из пакета java.lang.invoke:
  • MethodHandle — своего рода указатель на метод, эта вещь будет широко применяться в замыканиях и лямбда выражениях (честно говоря, я думал там будет сделано всё наиболее простым образом — лямбды будут преобразовываться к анонимным классам и дело с концом).
  • MethodType — представляет собой сигнатуру метода, а именно возвращаемое значение и список входных параметров.
  • MethodHandles — класс различных утилит для работы с методами.
  • Lookup — класс входит в состав класса MethodHandles и предоставляет интерфейс для поиска методов с заданными параметрами.

   Итак, перейдём к самому интересному — как же нам всё это поможет. Теперь код будет выглядеть так:
  1. import static java.lang.invoke.MethodHandles.*;
  2.  
  3. import java.lang.invoke.MethodHandle;
  4. import java.lang.invoke.MethodType;
  5.  
  6. class A {
  7. }
  8.  
  9. class B extends A {
  10. }
  11.  
  12. public class Test {
  13.   
  14.   public void call(A a) {
  15.     System.out.println("A");
  16.   }
  17.   
  18.   public void call(B b) {
  19.     System.out.println("B");
  20.   }
  21.   
  22.   public static void main(String[] argv) {
  23.     A b = new B();
  24.     Test test = new Test();
  25.         
  26.     try {
  27.       Lookup lookup = lookup();
  28.       MethodType mt = MethodType.methodType(void.class, b.getClass());
  29.       
  30.       MethodHandle mh = lookup.findVirtual(Test.class, "call", mt);
  31.       mh.invokeWithArguments(test, b);
  32.     } catch (Throwable e) {
  33.       e.printStackTrace();
  34.     }
  35.   }
  36. }
* This source code was highlighted with Source Code Highlighter.

   Посмотрим, что же тут происходит. В строке 27 объявляем непосредственно «поисковик» функций. Далее в строке 28 объявляем типы возвращаемого значения и параметров. Важно отметить, в метод methodType сначала передаётся тип возвращаемого значения, затем типы списка параметров искомого метода. Строка 30 — получаем в MethodHandle результаты поиска методов в классе Test, с названием call и типами возвращаемого значения и параметров mt. В строке 31 вызываем найденный метод у объекта test с параметром b и видим на экране ожидаемое «B».

   В классе Lookup есть ещё довольно много методов, например, для поиска статических функций, но изучение этих фич остаётся читателю в качестве домашнего задания.
Теги:
Хабы:
+16
Комментарии 49
Комментарии Комментарии 49

Публикации

Истории

Работа

Java разработчик
359 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн