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

Комментарии 49

Мне кажеться что первый вариант выглядит понятие и красивее.
Приводить к нужному типу? А если вы не знаете, сколько будет таких типов? У нас была такая задача.
Тоже сталкивался с этим, приходилось решать с помощью аннотаций в аргументах.
> У нас была такая задача.

То есть все имена сторонних классов были известны внутри вашего класса-диспетчера, а чтобы выполнить какое-нибудь действие с переданным классом, вы сначала должны были узнать, к какому типу принадлежит объект «if (b instanceof B)» и только после этого производить с ним какие-то действия?

А вы не пробовали провести рефакторинг, заменив условный оператор (гигантский switch по типу передаваемого класса) полиморфизмом, в частности, образцами проектирования State|Strategy? Это даёт две вещи: избавление от необходимости слежения за появлением новых классов, передающихся в условный селектор (селектора больше не будет) во всех местах программы, где такая условнойсть присутствовала и дописываю новых case для них;
резко сокращается число зависимостей в программе и упрощается сопровождение кода, так как ввашему классу больше не нужно хранить информацию об используемых классах. Книга Мартина Фаулера «Рефакторинг. Улучшение существующего кода», СПб: Символ-Плюс, 2003,. в помощь.

Диспетчеризации действий c определением типа класса (instanceof) можно и нужно избегать.
НЛО прилетело и опубликовало эту надпись здесь
Достаточно хранить Map<Class, StrategyObject>. Потом просто вызывать getClass(), получить стратегию и вызвать ее.
Подписываюсь под каждым словом — если дело в проекте доходит до такого, надо рефакторить. Чем больше работаю с Java, тем больше сознаю, что при выборе решения надо не только придумывать, как это можно сделать, но и учитывать особенности и ограничения языка, с которым работаешь. Java — не Scala и не Jython — и не надо заставлять ее играть роль того или другого. Java — это Java.

Автор явно хочет от Java динамического поведения, но при этом отказывается от каста в попытке обмануть себя. Мы все принимаем на веру, что каст — это плохо. Но переходить от каста к dynamic dispatch только ради того, чтобы убрать из кода каст — именно это и происходит в коде — имхо, неразумно. Да, предложенный способ дешевле, чем рефлекшн, но он все равно дороже, чем статическое связывание.

И да, автор, в C#, как и в C++, и в Java, overloading производится на этапе компиляции, учите матчасть: en.wikipedia.org/wiki/Method_overloading
Ну не сказал бы. Как по мне, первый вариант не дает возможности реализовать просто паттерны вроде Фабрики.
Мне кажется, здесь показано не то применение MethodHandle. Я читал статью, где его применяли как замену Reflection в том случае, когда необходимо было вызывать приватные методы. MethodHandle должен работать быстрее Reflection, и он также позволяет динамическим языкам быстрее работать на JVM. В данном случае, как писали ниже, скорее более подойдет просто рефакторинг
Приводить к нужному типу? А если вы не знаете, сколько будет таких типов? У нас была такая задача.
Сорри, не туда.
Может я чего не понимаю, но вы же явно указываете, что b имеет тип A:

A b = new B();

но потом пишите:

> По правилам Java будет вызван метод public void call(A a), и соответственно выведено на экран «A», что может показаться странным, потому что реальный тип объекта B.
Java так работает, да, но нередко бывают задачи, когда надо взывать тот метод, который подходит для реального типа объекта, а не тип ссылки на объект. Здесь в ссылке типа A содержится реально объект типа B, и надо вызвать соответствующий метод, который принимает аргументом B, а не A. Общее решение стало доступно только в Java 7.
А в чем отличие от reflection?
Ну мне кажется этот вариант более лаконичен
download.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html

В данном случае нет никакой разницы, классы MethodType и MethodHandler введены для того, что бы обработать вызов invokedynamic «methodname» paramtypes.

Линкуется все это дело с использованием вызова bootstrap метода.

Пример. Есть bootstrap метод:
public static CallSite mybsm(
MethodHandles.Lookup callerClass, String dynMethodName, MethodType dynMethodType)

Есть код:
invokedynamic «add» "(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer)"

При выполнении кода (если для «add» он первый раз выполняется) будет вызван bootstrap метод с параметрами dynMethodName=«add», MethodType = MethodType.methodType(Integer.class, Integer.class, Integer.class), callerClass — ссылка на текущий lookup.

mybsm должен вернуть CallSite со ссылкой на MethodHandler, который соответствует названию (dynMethodName) и типу (dynMethodType). Далее происходит вызов invokeWithArguments, например MethodHandler'a.

Т.е. метод в invokedynamic линкуется с использованием bootstrap метода.
Собственно это и есть основная задача «the Da Vinci Machine Project», а не создание «лаконичного» аналога reflection api, как Вы написали.
полностью поддерживаю, имхо, автор несколько недопонял смысл введения этой конструкции.

Судя по тому что писал John Rose в своем блоге во время разработке, все это дело реализовывалось в рамках «универсализации» JVM.
В новой модели, предполагается компиляция в байткод всяческих динамически-типизуемых языков. И так как модель вызова методов классов, писаных на этих языках, достаточно непонятна, эти методы будут выносится в constant pool, class-файлов, во время компиляции. Формат class-файлов был специально под это изменен.

При разгрузке этих классов в JVM, эта информация будет подгружаться на линковке и будет происходить динамической связывание правил вызовов этих методов.

Детали лучше курить в новой спеки виртуалки.
С рефлекшеном еще бы пришлось динамически кастовать объект к своему реальному типу.
А с новым API можно делать обработку вызовов классом в зависимости от того, как называется метод?
То есть перейти от вызова методов к message-passing по сути?
"(в отличии, скажем, от C#, где это делается во время исполнения)"

Но это никак не значит, что в .Net вызовется метод «public void call(B b)», там такое же правило действует — как объявлен объект с такими параметрами метод и вызывается. Кроме рефлекции, обойти в C# можно ещё объявив объект ключевым словом dynamic, но со всеми вытекающими динамических объектов…

dynamic b = new B();
Test test = new Test();
test.call(b);
Вызовет «public void call(B b)»

P.S. И если честно, я не понял преимуществ такого количества кода перед рефлекцией. Опять же в C#:
Type testType = test.GetType();
MethodInfo mi = testType.GetMethod(«call», new Type[] { b.GetType() });
mi.Invoke(test, new object[]{ b });
Если в Java рефлекция появилась только в 7 версии мне печально =)
Если мне память не изменяет, оно еще в 1998 году было.
Тогда наверно я не понял в чем заключается новшество «the Da Vinci Machine»
Как я понял из описания проекта, основная его фишка это:

We are extending the JVM with first-class architectural support for languages other than Java, especially dynamic languages. This project will prototype a number of extensions to the JVM, so that it can run non-Java languages efficiently, with a performance level comparable to that of Java itself.

Sub-projects with major activity include dynamic invocation, continuations, tail-calls, and interface injection.
Ага поддержка динамических языков + «dynamic invocation, continuations, tail-calls, and interface injection»
Но тогда последний вопрос в 6-ой версии Java нельзя был ополучить тип класса и по имени метода сам метод и вызвать его? Это можно тока в седьмой версии которыая привнесла
«the Da Vinci Machine Project»
> Но тогда последний вопрос в 6-ой версии Java нельзя был ополучить тип класса и по имени метода сам метод и вызвать его?

Что-то типа такого?

test.getClass().getMethod(«call», B.class).invoke(test, b);
Ясно. Ваш метод явно лаконичнее предложеного автором статьи.
Только, честно, яхз работает ли он так как надо автору статьи :)
Нет :)

Тут вы явно указываете, что метод нужо взять из класса B, а автора интересует вариант, когда есть объект неизвестного класса и нужно сначала определить, из какого же класса в иерархии нужно вызвать метод.
test.getClass().getMethod(«call», b.getClass()).invoke(test, b) не будет работать?
Так под капотом это будет вызывать рефлексию со всеми вытекающими (медленно, засирается PermGen) или что-то особое?
Честно сказать — не знаю, ещё не ковырял :) Сегодня утром сел разобраться что там вообще есть в седьмой джаве новое и вот решил поделиться результатом.
Что-то особое, в частности новую JVM инструкцию invokedynamic
Тип объекта не определяется, однако метод вызвается верный…

Ах да… Это стандартная заковыка тестов :) И уже подзабыл… Жаль, что на работе пишу на C#, хотелось бы больше времени уделять Джаве…
Я наверное не совсем понял проблему, описаную в примере и вполне заслуживаю большой минус, но зачем делать кучу методов типа call(A a), call(B b), call(C c), когда можно в классах A, B, C определить метод print().

Конечно, если эту возможность ввели в java7, то наверное это кому-нибудь нужно, но из приведенного примера это, на мой взгляд, как-то неочевидно.
ну хотя бы потому, что вариантов реализации метода print() может быть несколько. например, понадобилось сделать печать не на экран, а на принтер — и что, теперь у всех классов переписывать print()?
Все равно не понимаю. Если у вас непостредственно выводом на экран/принтер занимаются классы A, B, C (или ф-ции call), то их так и так переписывать придется. А если они лишь вызывают некий API для печати, то для описанного случая вроде шаблон существуют — если не изменяет память вроде он Bridge называется.
В этом случае не нади ничего переписывать — просто выставляем нужный «printer» и готово.
Ну вот этот вот «вызов API» — это, по моему мнению, лишний код. Если объект просто «вызывает API», то этот самый вызов лучше из объекта вынести в специальный сервис.

Это бывает полезно, если данные передаются между разными уровнями. Например, на веб-уровне элемент можно отрендерить, а на db-уровне его можно сохранить. Альтернативой будет написание всех методов типа render(), save(), load() и т.д. у каждого объекта, либо использование разных объектов на разных уровнях.

>> } catch (Throwable e) {

Я понимаю, что это набросок кода, но все-таки так лучше не делать, потому что под этот catch попадают такие ошибки, как OutOfMemoryException и др.
В C# такое делается так:

test.call((dynamic)b);

Эта фича тоже появилась как побочный эффект поддержки динамических языков в .net (DLR) и поддержки этого в C# (тип dynamic).
Известно, что в Java решение о том, какой из перегруженных методов вызывать производится на этапе компиляции
можно здесь поподробнее и про раннее/позднее связывание?
интересно было бы взять готовое решение из commons-lang (которое через reflection работает) и сравнить производительность
> Известно, что в Java решение о том, какой из перегруженных методов вызывать производится на этапе компиляции

Это неправда. Не всегда однозначно определено, какой метод вызывать. Поэтому почти все вызовы компилируеются в invokevirtual.
в случае, если речь идет о перегрузке в смысле override, а не overload, то на сколько я помню, все обстоит именно так, как сказал автор.
В чем разница между разница между override и overload? Во что еще, кроме invokevirtual, может компилироваться вызов обычного метода?
в примере автора нет никаких виртуальных перегрузок, и судя по всему он подразумевал переопределение.
боюсь что начну заниматься интерпретацией слов автора, а это не есть гуд. но думаю он поправит если что.

как я понял, подразумевалось что при оверлоаде методов, как приведено в примере, компайлер делает раннее связывание вызовов методов. для манипуляциями с оверлоадами в делфяке например есть специальная дириктива, и некоторые пляски через это. если я все правильно понял, автор об этом и говорил, если же нет, то коммент автора о том что он имел ввиду в студию.

что касается invokevirtual, есть ещё invokerspecial и invokeinterface, которые к нашей ситуации не подходят вроде, да я о нем и не говорил :)
тьфу блин, overload c override местами перепутал.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории