Объявление типов в Groovy - PullRequest
7 голосов
/ 17 марта 2011

Когда вы не объявляете тип для переменной в groovy, я понимаю, что виртуальная машина Java должна использовать отражение, чтобы выяснить, какого типа объект перед выполнением каких-либо методов и может лиошибка во время выполнения.

Если это правильно, что делать, когда вы объявляете тип?Виртуальная машина Java все еще в конечном итоге использует отражение, потому что исходный код был в отличной форме?Или, во-первых, мое понимание всего этого вопроса неверно?

Ответы [ 5 ]

22 голосов
/ 17 марта 2011

Лучший способ исследовать проблему такого рода - это посмотреть на сгенерированный байт-код самостоятельно.Если вы создаете два примера классов:

WithType.groovy:

class WithType {
    String name

    String returnName() { getName() }
}

WithoutType.groovy:

class WithoutType {
    def name

    def returnName() { getName() }
}

Скомпилируйте их с помощью groovyc:

% groovyc WithType.groovy
% groovyc WithoutType.groovy

А затем используйте javap, чтобы выплюнуть читабельный для человека байт-код:

% javap -c WithType > WithType.txt
% javap -c WithoutType > WithoutType.txt

Затем вы можете просмотреть 2 файла и найти метод returnName(), чтобы увидеть, как они обрабатываются.иначе как они вызывают сгенерированный getName() метод для поля имени.Если вы найдете метод returnName(), вы увидите, что версия WithType выглядит следующим образом:

public java.lang.String returnName();
  Code:
   0:   invokestatic    #24; //Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
   3:   astore_1
   4:   aload_1
   5:   ldc #47; //int 0
   7:   aaload
   8:   aload_0
   9:   invokeinterface #53,  2; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;)Ljava/lang/Object;
   14:  invokestatic    #56; //Method $get$$class$java$lang$String:()Ljava/lang/Class;
   17:  invokestatic    #38; //Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType:(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
   20:  checkcast   #58; //class java/lang/String
   23:  areturn
   24:  nop

, а нетипизированная выглядит так:

public java.lang.Object returnName();
  Code:
   0:   invokestatic    #24; //Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
   3:   astore_1
   4:   aload_1
   5:   ldc #47; //int 0
   7:   aaload
   8:   aload_0
   9:   invokeinterface #53,  2; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;)Ljava/lang/Object;
   14:  areturn
   15:  nop

Нетипизированнаяимеет меньше инструкций, потому что ему не нужно выполнять какую-либо проверку типа или преобразование строки в том, что он возвращает.Фактический вызов метода для getName() одинаков как в типизированной, так и в нетипизированной версиях:

   9:   invokeinterface #53,  2; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;)Ljava/lang/Object;

Вы можете видеть, что он вызывает интерфейсный метод в методе Groovy CallSite и передает GroovyObject. CallSite - это интерфейс, который реализован с помощью отличного кода метаобъекта.Так что это вызов Groovy MOP, который динамически вызывает метод getName().

(это все с groovy 1.7.5)

7 голосов
/ 17 марта 2011

Не похоже, что объявления типов имеют какое-то конкретное влияние на то, как Groovy вызывает метод.По сути, как отмечалось в Groovy wiki , вместо простого вызова метода Groovy вызовет invokeMethod() для метакласса объекта, который либо делегирует методу, определенному в метаклассе, либо выполняет рефлексивный поискmethod.

Стоит отметить, что метакласс использует MetaMethod , который в худшем случае использует поиск в кэшированном отражении, т. е. ему нужен только один отражающий поиск.

Редактировать

Некоторые из этих издержек можно избежать с помощью groovypp , который добавляет статические возможности ввода в код Groovy.

2 голосов
/ 17 марта 2011

По-прежнему интенсивно используется отражение.
Одна из вещей, которую вы должны иметь в виду, - это то, что вызовы методов разрешаются во время выполнения.
Самый простой способ увидеть это

Integer.metaClass.xxx << {println "hi"}
Integer ten = 10
ten.xxx()

Это компилируется, даже если обычное Integer 100 не имеет метода xxx.
Первая строка добавляет метод в класс, но компилятор не узнает об этом во время компиляции (метод добавляется во время выполнения).
Неважно, что тип известен, groovy все еще использует отражение для выполнения вызовов.
Другой пример.

def (Number i, Number l) = [100, 100L]
print i
print l
def print(Number n)  {println "Number $n"}
def print(Integer n) {println "Integer $n"}

В Java, он напечатал бы число 100 дважды, так как метод статическиselected.
Groovy это не волнует, он просто выбирает метод на основе класса аргументов во время выполнения.

0 голосов
/ 22 марта 2012

Есть несколько диаграмм, чтобы ответить на ваш вопрос от создателей groovy ++:

https://code.google.com/p/groovypptest/wiki/Performance

Они показывают, что Groovy работает медленно (в зависимости от алгоритма в 10-100 раз медленнее). Это связано не только с динамической типизацией, чрезмерным использованием отражения и динамически вызываемыми методами. Он обрабатывает объектные массивы как списки. Так что он полностью работает с обработкой Java-массивов. Что делает его также медленнее, чем Java. И даже Groovy ++ (самый простой способ оптимизировать Groovy программы) не решает этот факт.

Но независимо от того, насколько медленно работает Groovy-код, мне действительно нравятся возможности языка и тот факт, что вы можете заменить тонны строк Jave несколькими строками Groovy. У каждого языка есть своя цель. Java хорош для гибкости и масштабируемости, ruby ​​- для быстрого прототипирования (не столько для масштабирования), а Groovy - очень хорошее решение для написания быстрых прототипов со всеми функциями java, которые почти так же масштабируемы, как и код java (также вы можете заменить медленно работающий отличный код шаг за шагом с Java или G ++, как вам нужно).

0 голосов
/ 17 марта 2011

Привет, это был хороший вопрос, но я не знаю, использует ли JVM отражение, чтобы найти эти типы.Когда вы кодируете groovy, вам нужно пройти через то, что Java и Groovy отличаются только исходным кодом .Когда они были запущены, они были привязаны к объектной модели Java.Независимо от того, пишете ли вы классы Groovy или скрипты, они работают как классы Java внутри JVM.Поэтому независимо от того, что вы делаете, все они конвертируются в Java и запускаются в JVM:)

Хотя среда выполнения Java без проблем понимает скомпилированные классы Groovy, она не понимает исходные файлы .groovy.Если вы хотите динамически загружать файлы .groovy во время выполнения, вам придется потратить больше времени.Убедитесь, что синтаксис Groovy ориентирован на строки , а выполнение - нет.То есть Groovy-код не обрабатывается Строка-за-строкой (в шоке! Тебе придется, уловка продолжается) Вместо этого Groovy полностью анализируется, и создается класс.Этот сгенерированный класс действует как мост между Java и Groovy. Классы Groovy генерируются так, что их формат идентичен байт-коду Java.Итак, как я упоминал ранее, поскольку Groovy создает один и тот же байт-код и работает в Jvm, загрузчик классов может обрабатывать и этот байт-код.Если это звучит грубо, не волнуйтесь, все это делает Groovy для нас.

Рекомендую прочитать Groovy в действии книгу.Нажмите здесь

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