Комментарии 49
Мне кажеться что первый вариант выглядит понятие и красивее.
+8
Приводить к нужному типу? А если вы не знаете, сколько будет таких типов? У нас была такая задача.
+3
Тоже сталкивался с этим, приходилось решать с помощью аннотаций в аргументах.
0
> У нас была такая задача.
То есть все имена сторонних классов были известны внутри вашего класса-диспетчера, а чтобы выполнить какое-нибудь действие с переданным классом, вы сначала должны были узнать, к какому типу принадлежит объект «if (b instanceof B)» и только после этого производить с ним какие-то действия?
А вы не пробовали провести рефакторинг, заменив условный оператор (гигантский switch по типу передаваемого класса) полиморфизмом, в частности, образцами проектирования State|Strategy? Это даёт две вещи: избавление от необходимости слежения за появлением новых классов, передающихся в условный селектор (селектора больше не будет) во всех местах программы, где такая условнойсть присутствовала и дописываю новых case для них;
резко сокращается число зависимостей в программе и упрощается сопровождение кода, так как ввашему классу больше не нужно хранить информацию об используемых классах. Книга Мартина Фаулера «Рефакторинг. Улучшение существующего кода», СПб: Символ-Плюс, 2003,. в помощь.
Диспетчеризации действий c определением типа класса (instanceof) можно и нужно избегать.
То есть все имена сторонних классов были известны внутри вашего класса-диспетчера, а чтобы выполнить какое-нибудь действие с переданным классом, вы сначала должны были узнать, к какому типу принадлежит объект «if (b instanceof B)» и только после этого производить с ним какие-то действия?
А вы не пробовали провести рефакторинг, заменив условный оператор (гигантский switch по типу передаваемого класса) полиморфизмом, в частности, образцами проектирования State|Strategy? Это даёт две вещи: избавление от необходимости слежения за появлением новых классов, передающихся в условный селектор (селектора больше не будет) во всех местах программы, где такая условнойсть присутствовала и дописываю новых case для них;
резко сокращается число зависимостей в программе и упрощается сопровождение кода, так как ввашему классу больше не нужно хранить информацию об используемых классах. Книга Мартина Фаулера «Рефакторинг. Улучшение существующего кода», СПб: Символ-Плюс, 2003,. в помощь.
Диспетчеризации действий c определением типа класса (instanceof) можно и нужно избегать.
+2
НЛО прилетело и опубликовало эту надпись здесь
Подписываюсь под каждым словом — если дело в проекте доходит до такого, надо рефакторить. Чем больше работаю с Java, тем больше сознаю, что при выборе решения надо не только придумывать, как это можно сделать, но и учитывать особенности и ограничения языка, с которым работаешь. Java — не Scala и не Jython — и не надо заставлять ее играть роль того или другого. Java — это Java.
Автор явно хочет от Java динамического поведения, но при этом отказывается от каста в попытке обмануть себя. Мы все принимаем на веру, что каст — это плохо. Но переходить от каста к dynamic dispatch только ради того, чтобы убрать из кода каст — именно это и происходит в коде — имхо, неразумно. Да, предложенный способ дешевле, чем рефлекшн, но он все равно дороже, чем статическое связывание.
И да, автор, в C#, как и в C++, и в Java, overloading производится на этапе компиляции, учите матчасть: en.wikipedia.org/wiki/Method_overloading
Автор явно хочет от Java динамического поведения, но при этом отказывается от каста в попытке обмануть себя. Мы все принимаем на веру, что каст — это плохо. Но переходить от каста к dynamic dispatch только ради того, чтобы убрать из кода каст — именно это и происходит в коде — имхо, неразумно. Да, предложенный способ дешевле, чем рефлекшн, но он все равно дороже, чем статическое связывание.
И да, автор, в C#, как и в C++, и в Java, overloading производится на этапе компиляции, учите матчасть: en.wikipedia.org/wiki/Method_overloading
+4
Ну не сказал бы. Как по мне, первый вариант не дает возможности реализовать просто паттерны вроде Фабрики.
+1
Мне кажется, здесь показано не то применение MethodHandle. Я читал статью, где его применяли как замену Reflection в том случае, когда необходимо было вызывать приватные методы. MethodHandle должен работать быстрее Reflection, и он также позволяет динамическим языкам быстрее работать на JVM. В данном случае, как писали ниже, скорее более подойдет просто рефакторинг
0
Приводить к нужному типу? А если вы не знаете, сколько будет таких типов? У нас была такая задача.
0
Может я чего не понимаю, но вы же явно указываете, что b имеет тип A:
A b = new B();
но потом пишите:
> По правилам Java будет вызван метод public void call(A a), и соответственно выведено на экран «A», что может показаться странным, потому что реальный тип объекта B.
A b = new B();
но потом пишите:
> По правилам Java будет вызван метод public void call(A a), и соответственно выведено на экран «A», что может показаться странным, потому что реальный тип объекта B.
+2
Java так работает, да, но нередко бывают задачи, когда надо взывать тот метод, который подходит для реального типа объекта, а не тип ссылки на объект. Здесь в ссылке типа A содержится реально объект типа B, и надо вызвать соответствующий метод, который принимает аргументом B, а не A. Общее решение стало доступно только в Java 7.
0
А в чем отличие от reflection?
+2
Ну мне кажется этот вариант более лаконичен
0
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, как Вы написали.
В данном случае нет никакой разницы, классы 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, как Вы написали.
0
полностью поддерживаю, имхо, автор несколько недопонял смысл введения этой конструкции.
Судя по тому что писал John Rose в своем блоге во время разработке, все это дело реализовывалось в рамках «универсализации» JVM.
В новой модели, предполагается компиляция в байткод всяческих динамически-типизуемых языков. И так как модель вызова методов классов, писаных на этих языках, достаточно непонятна, эти методы будут выносится в constant pool, class-файлов, во время компиляции. Формат class-файлов был специально под это изменен.
При разгрузке этих классов в JVM, эта информация будет подгружаться на линковке и будет происходить динамической связывание правил вызовов этих методов.
Детали лучше курить в новой спеки виртуалки.
Судя по тому что писал John Rose в своем блоге во время разработке, все это дело реализовывалось в рамках «универсализации» JVM.
В новой модели, предполагается компиляция в байткод всяческих динамически-типизуемых языков. И так как модель вызова методов классов, писаных на этих языках, достаточно непонятна, эти методы будут выносится в constant pool, class-файлов, во время компиляции. Формат class-файлов был специально под это изменен.
При разгрузке этих классов в JVM, эта информация будет подгружаться на линковке и будет происходить динамической связывание правил вызовов этих методов.
Детали лучше курить в новой спеки виртуалки.
0
С рефлекшеном еще бы пришлось динамически кастовать объект к своему реальному типу.
+1
А с новым API можно делать обработку вызовов классом в зависимости от того, как называется метод?
То есть перейти от вызова методов к message-passing по сути?
То есть перейти от вызова методов к message-passing по сути?
0
"(в отличии, скажем, от 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 });
Но это никак не значит, что в .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 });
+4
Если в Java рефлекция появилась только в 7 версии мне печально =)
-6
Если мне память не изменяет, оно еще в 1998 году было.
+3
Тогда наверно я не понял в чем заключается новшество «the Da Vinci Machine»
0
Как я понял из описания проекта, основная его фишка это:
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.
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.
0
Ага поддержка динамических языков + «dynamic invocation, continuations, tail-calls, and interface injection»
Но тогда последний вопрос в 6-ой версии Java нельзя был ополучить тип класса и по имени метода сам метод и вызвать его? Это можно тока в седьмой версии которыая привнесла
«the Da Vinci Machine Project»
Но тогда последний вопрос в 6-ой версии Java нельзя был ополучить тип класса и по имени метода сам метод и вызвать его? Это можно тока в седьмой версии которыая привнесла
«the Da Vinci Machine Project»
0
> Но тогда последний вопрос в 6-ой версии Java нельзя был ополучить тип класса и по имени метода сам метод и вызвать его?
Что-то типа такого?
test.getClass().getMethod(«call», B.class).invoke(test, b);
Что-то типа такого?
test.getClass().getMethod(«call», B.class).invoke(test, b);
+3
Ясно. Ваш метод явно лаконичнее предложеного автором статьи.
0
Только, честно, яхз работает ли он так как надо автору статьи :)
+1
Шикарно!
0
Так под капотом это будет вызывать рефлексию со всеми вытекающими (медленно, засирается PermGen) или что-то особое?
+1
Тип объекта не определяется, однако метод вызвается верный…
Ах да… Это стандартная заковыка тестов :) И уже подзабыл… Жаль, что на работе пишу на C#, хотелось бы больше времени уделять Джаве…
Ах да… Это стандартная заковыка тестов :) И уже подзабыл… Жаль, что на работе пишу на C#, хотелось бы больше времени уделять Джаве…
+1
Я наверное не совсем понял проблему, описаную в примере и вполне заслуживаю большой минус, но зачем делать кучу методов типа call(A a), call(B b), call(C c), когда можно в классах A, B, C определить метод print().
Конечно, если эту возможность ввели в java7, то наверное это кому-нибудь нужно, но из приведенного примера это, на мой взгляд, как-то неочевидно.
Конечно, если эту возможность ввели в java7, то наверное это кому-нибудь нужно, но из приведенного примера это, на мой взгляд, как-то неочевидно.
+2
ну хотя бы потому, что вариантов реализации метода print() может быть несколько. например, понадобилось сделать печать не на экран, а на принтер — и что, теперь у всех классов переписывать print()?
0
Все равно не понимаю. Если у вас непостредственно выводом на экран/принтер занимаются классы A, B, C (или ф-ции call), то их так и так переписывать придется. А если они лишь вызывают некий API для печати, то для описанного случая вроде шаблон существуют — если не изменяет память вроде он Bridge называется.
В этом случае не нади ничего переписывать — просто выставляем нужный «printer» и готово.
В этом случае не нади ничего переписывать — просто выставляем нужный «printer» и готово.
0
Ну вот этот вот «вызов API» — это, по моему мнению, лишний код. Если объект просто «вызывает API», то этот самый вызов лучше из объекта вынести в специальный сервис.
Это бывает полезно, если данные передаются между разными уровнями. Например, на веб-уровне элемент можно отрендерить, а на db-уровне его можно сохранить. Альтернативой будет написание всех методов типа render(), save(), load() и т.д. у каждого объекта, либо использование разных объектов на разных уровнях.
Это бывает полезно, если данные передаются между разными уровнями. Например, на веб-уровне элемент можно отрендерить, а на db-уровне его можно сохранить. Альтернативой будет написание всех методов типа render(), save(), load() и т.д. у каждого объекта, либо использование разных объектов на разных уровнях.
0
>> } catch (Throwable e) {
Я понимаю, что это набросок кода, но все-таки так лучше не делать, потому что под этот catch попадают такие ошибки, как OutOfMemoryException и др.
Я понимаю, что это набросок кода, но все-таки так лучше не делать, потому что под этот catch попадают такие ошибки, как OutOfMemoryException и др.
+1
В C# такое делается так:
Эта фича тоже появилась как побочный эффект поддержки динамических языков в .net (DLR) и поддержки этого в C# (тип dynamic).
test.call((dynamic)b);
Эта фича тоже появилась как побочный эффект поддержки динамических языков в .net (DLR) и поддержки этого в C# (тип dynamic).
0
Известно, что в Java решение о том, какой из перегруженных методов вызывать производится на этапе компиляции
можно здесь поподробнее и про раннее/позднее связывание?
можно здесь поподробнее и про раннее/позднее связывание?
0
интересно было бы взять готовое решение из commons-lang (которое через reflection работает) и сравнить производительность
0
> Известно, что в Java решение о том, какой из перегруженных методов вызывать производится на этапе компиляции
Это неправда. Не всегда однозначно определено, какой метод вызывать. Поэтому почти все вызовы компилируеются в invokevirtual.
Это неправда. Не всегда однозначно определено, какой метод вызывать. Поэтому почти все вызовы компилируеются в invokevirtual.
0
в случае, если речь идет о перегрузке в смысле override, а не overload, то на сколько я помню, все обстоит именно так, как сказал автор.
0
В чем разница между разница между override и overload? Во что еще, кроме invokevirtual, может компилироваться вызов обычного метода?
0
в примере автора нет никаких виртуальных перегрузок, и судя по всему он подразумевал переопределение.
0
боюсь что начну заниматься интерпретацией слов автора, а это не есть гуд. но думаю он поправит если что.
как я понял, подразумевалось что при оверлоаде методов, как приведено в примере, компайлер делает раннее связывание вызовов методов. для манипуляциями с оверлоадами в делфяке например есть специальная дириктива, и некоторые пляски через это. если я все правильно понял, автор об этом и говорил, если же нет, то коммент автора о том что он имел ввиду в студию.
что касается invokevirtual, есть ещё invokerspecial и invokeinterface, которые к нашей ситуации не подходят вроде, да я о нем и не говорил :)
как я понял, подразумевалось что при оверлоаде методов, как приведено в примере, компайлер делает раннее связывание вызовов методов. для манипуляциями с оверлоадами в делфяке например есть специальная дириктива, и некоторые пляски через это. если я все правильно понял, автор об этом и говорил, если же нет, то коммент автора о том что он имел ввиду в студию.
что касается invokevirtual, есть ещё invokerspecial и invokeinterface, которые к нашей ситуации не подходят вроде, да я о нем и не говорил :)
0
тьфу блин, overload c override местами перепутал.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
the Da Vinci Machine Project в Java 7 и борьба с велосипедами