Добрый день, уважаемые хабражители!
Совсем недавно вышла ожидаемая многими Java 7. К сожалению, многих разочаровал состав нововведий, так как в него не попали различные очень ожидаемые вкусности вроде Project Lambda. Однако нововведений всё же много и сегодня я хотел бы немного остановиться на одном из важнейших — the Da Vinci Machine Project, который позволяет пользоваться динамическими языками на JVM более эффективно. Если говорит точнее, то рассматривать мы будем одну из частей the Da Vinci Machine Project — method handle. До конца проникнуться концепциями этой части языка я ещё не успел, но большинство людей вообще не понимают, зачем оно надо :) В статье я рассмотрю один use case, который Java-программистам знаком не по наслышке и родил, думаю, наибольшее число велосипедов ever. Он, конечно, касается перегрузки методов и передачи параметров по интерфейсу.
Известно, что в Java решение о том, какой из перегруженных методов вызывать производится на этапе компиляции (в отличии, скажем, от C#, где это делается во время исполнения). Предположим, у нас есть следующий код:
По правилам Java будет вызван метод
Данный подход вполне имеет право на жизнь, но если у вас несколько десятков таких классов, то код становится банально уродливым, появляется дублирование и вообще.
В Java 7 появились средства, которые могут помочь решить данную проблему, если не на уровне языка, то хотя бы не уровне костылей. Итак, помогут нам классы из пакета
Итак, перейдём к самому интересному — как же нам всё это поможет. Теперь код будет выглядеть так:
Посмотрим, что же тут происходит. В строке 27 объявляем непосредственно «поисковик» функций. Далее в строке 28 объявляем типы возвращаемого значения и параметров. Важно отметить, в метод
В классе
Совсем недавно вышла ожидаемая многими Java 7. К сожалению, многих разочаровал состав нововведий, так как в него не попали различные очень ожидаемые вкусности вроде Project Lambda. Однако нововведений всё же много и сегодня я хотел бы немного остановиться на одном из важнейших — the Da Vinci Machine Project, который позволяет пользоваться динамическими языками на JVM более эффективно. Если говорит точнее, то рассматривать мы будем одну из частей the Da Vinci Machine Project — method handle. До конца проникнуться концепциями этой части языка я ещё не успел, но большинство людей вообще не понимают, зачем оно надо :) В статье я рассмотрю один use case, который Java-программистам знаком не по наслышке и родил, думаю, наибольшее число велосипедов ever. Он, конечно, касается перегрузки методов и передачи параметров по интерфейсу.
Известно, что в Java решение о том, какой из перегруженных методов вызывать производится на этапе компиляции (в отличии, скажем, от C#, где это делается во время исполнения). Предположим, у нас есть следующий код:
- class A {
- }
-
- class B extends A {
- }
-
- public class Test {
-
- public void call(A a) {
- System.out.println("A");
- }
-
- public void call(B b) {
- System.out.println("B");
- }
-
- public static void main(String[] argv) {
- A b = new B();
- Test test = new Test();
- test.call(b);
- }
- }
* This source code was highlighted with Source Code Highlighter.
По правилам Java будет вызван метод
public void call(A a)
, и соответственно выведено на экран «A», что может показаться странным, потому что реальный тип объекта B
. Для того, чтобы вызвать нужный нам метод необходимо переписать код вызова метода так:
- if (b instanceof B) {
- test.call((B) b);
- }
* This source code was highlighted with Source Code Highlighter.
Данный подход вполне имеет право на жизнь, но если у вас несколько десятков таких классов, то код становится банально уродливым, появляется дублирование и вообще.
В Java 7 появились средства, которые могут помочь решить данную проблему, если не на уровне языка, то хотя бы не уровне костылей. Итак, помогут нам классы из пакета
java.lang.invoke
: MethodHandle
— своего рода указатель на метод, эта вещь будет широко применяться в замыканиях и лямбда выражениях (честно говоря, я думал там будет сделано всё наиболее простым образом — лямбды будут преобразовываться к анонимным классам и дело с концом).MethodType
— представляет собой сигнатуру метода, а именно возвращаемое значение и список входных параметров.MethodHandles
— класс различных утилит для работы с методами.Lookup
— класс входит в состав классаMethodHandles
и предоставляет интерфейс для поиска методов с заданными параметрами.
Итак, перейдём к самому интересному — как же нам всё это поможет. Теперь код будет выглядеть так:
- import static java.lang.invoke.MethodHandles.*;
-
- import java.lang.invoke.MethodHandle;
- import java.lang.invoke.MethodType;
-
- class A {
- }
-
- class B extends A {
- }
-
- public class Test {
-
- public void call(A a) {
- System.out.println("A");
- }
-
- public void call(B b) {
- System.out.println("B");
- }
-
- public static void main(String[] argv) {
- A b = new B();
- Test test = new Test();
-
- try {
- Lookup lookup = lookup();
- MethodType mt = MethodType.methodType(void.class, b.getClass());
-
- MethodHandle mh = lookup.findVirtual(Test.class, "call", mt);
- mh.invokeWithArguments(test, b);
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
- }
* This source code was highlighted with Source Code Highlighter.
Посмотрим, что же тут происходит. В строке 27 объявляем непосредственно «поисковик» функций. Далее в строке 28 объявляем типы возвращаемого значения и параметров. Важно отметить, в метод
methodType
сначала передаётся тип возвращаемого значения, затем типы списка параметров искомого метода. Строка 30 — получаем в MethodHandle
результаты поиска методов в классе Test
, с названием call
и типами возвращаемого значения и параметров mt
. В строке 31 вызываем найденный метод у объекта test
с параметром b
и видим на экране ожидаемое «B».В классе
Lookup
есть ещё довольно много методов, например, для поиска статических функций, но изучение этих фич остаётся читателю в качестве домашнего задания.