Apache Groovy — это объектно-ориентированный язык программирования , совместимый с синтаксисом Java , для платформы Java . Это одновременно статический и динамический язык с функциями, аналогичными функциям Python , Ruby и Smalltalk . Его можно использовать как язык программирования и язык сценариев для платформы Java, он компилируется в байт-код виртуальной машины Java (JVM) и легко взаимодействует с другим кодом и библиотеками Java . Groovy использует синтаксис фигурных скобок, аналогичный Java. Groovy поддерживает замыкания , многострочные строки и выражения, встроенные в строки . Большая часть возможностей Groovy заключается в преобразованиях AST , запускаемых посредством аннотаций.
Groovy 1.0 был выпущен 2 января 2007 г., а Groovy 2.0 — в июле 2012 г. Начиная с версии 2, Groovy можно компилировать статически , предлагая определение типа и производительность, близкую к Java. [3] [4] Groovy 2.4 был последним крупным выпуском, выпущенным при спонсорской поддержке Pivotal Software , которая закончилась в марте 2015 года. [5] С тех пор Groovy изменил свою структуру управления на Комитет по управлению проектами в Apache Software Foundation . [6]
Джеймс Страчан впервые рассказал о разработке Groovy в своем блоге в августе 2003 года. [7] В марте 2004 года Groovy был представлен JCP как JSR 241 [8] и принят голосованием. В период с 2004 по 2006 год было выпущено несколько версий. После начала усилий по стандартизации Java Community Process (JCP) нумерация версий изменилась, и 2 января 2007 года была выпущена версия под названием «1.0». После различных бета-версий и кандидатов на выпуск под номером 1.1, 7 декабря 2007 г. был выпущен Groovy 1.1 Final, номер которого сразу же был изменен на Groovy 1.5, чтобы отразить множество внесенных изменений.
В 2007 году Groovy получил первый приз на премии инноваций JAX 2007. [9] В 2008 году Grails , веб-фреймворк Groovy , получил вторую премию на премии JAX 2008 Innovation Award. [10]
В ноябре 2008 года SpringSource приобрела компанию Groovy and Grails (G2One). [11] В августе 2009 года VMware приобрела SpringSource. [12]
В апреле 2012 года, после восьми лет бездействия, руководитель спецификации изменил статус JSR 241 на неактивный. [8]
Стрэчан молча покинул проект за год до выпуска Groovy 1.0 в 2007 году . [13]
2 июля 2012 года был выпущен Groovy 2.0, в котором, помимо других новых функций, были добавлены статическая компиляция и статическая проверка типов .
Когда в апреле 2013 года совместное предприятие Pivotal Software было выделено корпорациями EMC (EMC) и VMware, Groovy и Grails стали частью его портфеля продуктов. Pivotal прекратила спонсировать Groovy и Grails с апреля 2015 года . [5] В том же месяце Groovy изменила свою структуру управления с репозитория Codehaus на Комитет управления проектами (PMC) в Apache Software Foundation через свой инкубатор. [6] Groovy окончил инкубатор Apache и стал проектом высшего уровня в ноябре 2015 года. [14]
Большинство допустимых файлов Java также являются допустимыми файлами Groovy. Хотя эти два языка похожи, код Groovy может быть более компактным, поскольку ему не нужны все элементы, необходимые Java. [15] Это позволяет Java-программистам постепенно изучать Groovy, начиная со знакомого синтаксиса Java, а затем овладевая другими идиомами программирования Groovy . [16]
Функции Groovy, недоступные в Java, включают как статическую, так и динамическую типизацию (с ключевым словом def
), перегрузку операторов , собственный синтаксис для списков и ассоциативных массивов (карт), встроенную поддержку регулярных выражений , полиморфную итерацию, интерполяцию строк , добавленные вспомогательные методы и оператор безопасной навигации ?.
для автоматической проверки нулевых указателей (например, variable?.method()
или variable?.field
). [17]
Начиная с версии 2, Groovy также поддерживает модульность (возможность поставлять только необходимые jar-файлы в соответствии с потребностями проекта, тем самым уменьшая размер библиотеки Groovy), проверку типов, статическую компиляцию, улучшения синтаксиса Project Coin, блоки multicatch и постоянное повышение производительности с использованием invokedynamic
инструкция, представленная в Java 7 . [18]
Groovy обеспечивает встроенную поддержку различных языков разметки, таких как XML и HTML , посредством встроенного синтаксиса объектной модели документа (DOM). Эта функция позволяет определять и манипулировать многими типами гетерогенных данных с помощью единообразного и краткого синтаксиса и методологии программирования. [ нужна цитата ]
В отличие от Java, файл исходного кода Groovy может быть выполнен как (некомпилированный) скрипт , если он содержит код вне какого-либо определения класса, если это класс с основным методом или если он является Runnable или GroovyTestCase . Сценарий Groovy полностью анализируется, компилируется и генерируется перед выполнением (аналогично Python и Ruby). Это происходит скрыто, и скомпилированная версия не сохраняется как артефакт процесса. [19]
GroovyBeans — это версия JavaBeans от Groovy . Groovy неявно генерирует геттеры и сеттеры. В следующем коде setColor(String color)
и getColor()
генерируются неявно. Последние две строки, которые, по-видимому, напрямую обращаются к цвету, на самом деле вызывают неявно сгенерированные методы. [20]
класс AGroovyBean { цвет строки } def myGroovyBean = новый AGroovyBean () мойGroovyBean . setColor ( 'baby blue' ) утверждает myGroovyBean . getColor () == 'голубой' мойGroovyBean . color = 'олово' утверждает myGroovyBean . цвет == 'олово'
Groovy предлагает простой и согласованный синтаксис для обработки списков и карт , напоминающий синтаксис массивов Java . [21]
def MovieList = [ ' Дерсу Узала' , ' Ран ' , ' Семь самураев' ] // Выглядит как массив , но представляет собой список . Добавляет элемент в список Assert MovieList . размер () == 4 def MonthMap = [ ' Январь ' : 31 , ' Февраль ' : 28 , ' Март ' : 31 ] // Объявляет карту _ _ _ _ _ _ // Добавляет запись на карту Assert MonthMap . размер () == 4
Groovy предлагает поддержку расширения прототипа с помощью модулей расширения (только в Groovy 2), категорийExpandoMetaClass
, подобных Objective-C, и . [22]DelegatingMetaClass
ExpandoMetaClass
предлагает предметно-ориентированный язык (DSL) для простого выражения изменений в классе, аналогично концепции открытого класса Ruby :
Число . метакласс { sqrt = { Math . sqrt ( делегат ) } } утверждать 9 . sqrt () == 3 утверждать 4 . кврт () == 2
Изменения кода Groovy в результате прототипирования не видны в Java, поскольку каждый вызов атрибута/метода в Groovy проходит через реестр метаклассов. Доступ к измененному коду можно получить только из Java, перейдя в реестр метаклассов.
Groovy также позволяет переопределять методы getProperty()
, propertyMissing()
например, позволяя разработчику перехватывать вызовы объекта и указывать для него действие упрощенным аспектно-ориентированным способом. Следующий код позволяет классу java.lang.String
реагировать на hex
свойство:
enum Color { ЧЕРНЫЙ ( '#000000' ), БЕЛЫЙ ( '#FFFFFF' ), RED ( '#FF0000' ), СИНИЙ ( '#0000FF' ) String hex Color ( String hex ) { this . шестнадцатеричный = шестнадцатеричный } } Нить . метакласс . getProperty = { Строковое свойство -> def stringColor = делегат , если ( свойство == 'hex' ) { Color . ценности (). Найди это . _ имя (). EqualsIgnoreCase stringColor }?. шестнадцатеричный } } утверждать «БЕЛЫЙ» . hex == "#FFFFFF" утверждает "СИНИЙ" . hex == "#0000FF" утверждает "ЧЕРНЫЙ" . hex == "#000000" утверждает "ЗЕЛЕНЫЙ" . шестнадцатеричный == ноль
Платформа Grails широко использует метапрограммирование для обеспечения динамических средств поиска GORMUser.findByName('Josh')
, таких как и другие. [23]
Синтаксис Groovy позволяет в некоторых ситуациях опускать круглые скобки и точки. Следующий заводной код
взять ( кофе ). с ( сахаром , молоком ). и ( ликер )
можно записать как
пейте кофе с сахаром , молоком и ликером
позволяя разрабатывать предметно-ориентированные языки (DSL), которые выглядят как обычный английский.
Хотя Groovy в основном является объектно-ориентированным языком, он также предлагает функции функционального программирования .
Согласно документации Groovy: «Замыкания в Groovy работают аналогично «указателю метода», позволяя писать и запускать код в более поздний момент времени». [24] Замыкания Groovy поддерживают свободные переменные, то есть переменные, которые не были явно переданы ему в качестве параметра, но существуют в контексте его объявления, частичное применение (которое он называет « каррированием » [25] ), делегирование, неявное, типизированное и нетипизированные параметры.
При работе с коллекциями определенного типа можно вывести замыкание, переданное операции над коллекцией:
список = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] /* * Ненулевые числа приводятся к значению true, поэтому, когда % 2 == 0 (четное), оно является ложным. * Тип неявного параметра «it» может быть выведен средой IDE как целое число. * Это также можно записать так: * list.findAll { Integer i -> i % 2 } * list.findAll { i -> i % 2 } */ def chances = list . findAll { это % 2 } утверждать шансы == [ 1 , 3 , 5 , 7 , 9 ]
Группа выражений может быть записана в блоке замыкания без ссылки на реализацию, а отвечающий объект может быть назначен позже с помощью делегирования:
// Этот блок кода содержит выражения без ссылки на реализацию def Operations = { объявление 5 сумма 4 деление 3 печать }
/* * Этот класс будет обрабатывать операции, которые можно использовать в приведенном выше замыкании. Другой класс * может быть объявлен с теми же методами, но с использованием, например, операций * веб-сервиса в вычислениях. */ class Expression { значение BigDecimal /* * Хотя в качестве параметра передается целое число, оно преобразуется в BigDecimal, как было * определено. Если бы в классе был метод «объявить (целое значение)», вместо него использовался бы он. */ def декларировать ( значение BigDecimal ) { this . значение = значение } Защита сумма ( BigDecimal valueToAdd ) { this . значение += valueToAdd } Защита разделения ( делитель BigDecimal ) { this . значение /= делитель } def propertyMissing ( свойство String ) { if ( property == "print" ) println value } }
// Здесь определяется, кто будет отвечать на выражения в блоке кода выше. операции . делегат = новое выражение () операции ()
Эта функция Groovy, обычно называемая частичным применением , [25] позволяет устанавливать для параметров замыканий значения по умолчанию в любом из их аргументов, создавая новое замыкание со связанным значением. Предоставление одного аргумента методу curry()
исправит первый аргумент. Предоставление N аргументов зафиксирует аргументы 1 .. N.
def joinTwoWordsWithSymbol = { символ , первый , второй -> первый + символ + второй } утверждать joinTwoWordsWithSymbol ( '#' , 'Hello' , 'World' ) == 'Hello#World' защита concatWords = joinTwoWordsWithSymbol . curry ( ' ' ) Assert concatWords ( 'Hello' , 'World' ) == 'Hello World' защита prependHello = concatWords . curry ( 'Hello' ) //def prependHello = joinTwoWordsWithSymbol.curry(' ', 'Hello') Assert prependHello ( 'World' ) == 'Hello World'
Карри также можно использовать в обратном направлении (фиксируя последние N аргументов), используя rcurry()
.
def power = { значение BigDecimal , мощность BigDecimal -> значение ** мощность } квадрат защиты = сила . rcurry ( 2 ) def куб = мощность . ркарри ( 3 ) утверждать мощность ( 2 , 2 ) == 4 утверждать квадрат ( 4 ) == 16 утверждать куб ( 3 ) == 27
Groovy также поддерживает ленивые вычисления , [26] [27] сокращение/сложение , [28] бесконечные структуры и неизменяемость , [29] среди других. [30]
При нотации объектов JavaScript ( JSON ) и обработке XML Groovy использует шаблон Builder , что делает создание структуры данных менее многословным. Например, следующий XML:
<languages> <language Year= "1995" > <name> Java </name> <paradigm> объектно -ориентированный </paradigm> <typing> статический </typing> </language> <language Year= "1995" > <name > Ruby </name> <paradigm> функциональный, объектно- ориентированный </paradigm> <typing> утиная типизация, динамическая </typing> </language> < languageyear= "2003" > <name> Groovy </name> <paradigm > функциональный, объектно- ориентированный </paradigm> <typing> утиная типизация, динамическая, статическая </typing> </language> </languages>
можно сгенерировать с помощью следующего кода Groovy:
Защитный писатель = новый StringWriter () Защитный строитель = новый отличный . xml . Построитель MarkupBuilder ( писатель ) . языки { язык ( год: 1995 ) { название "Java" парадигма "объектно-ориентированная" типизация "статическая" } язык ( год: 1995 ) { название "Рубин" парадигма "функциональная, объектно-ориентированная" типизация "утиная типизация, динамическая" } язык ( год: 2003 ) { название "Groovy" парадигма "функциональная, объектно-ориентированная" типизация "утиная типизация, динамическая, статическая" } }
а также может обрабатываться в потоковом режиме через StreamingMarkupBuilder
. Чтобы изменить реализацию на JSON, MarkupBuilder
можно заменить его на JsonBuilder
. [31]
findAll
Для его анализа и поиска функционального языка может служить метод Groovy :
языки def = новый XmlSlurper (). автор parseText . нанизывать () // Здесь используется синтаксис регулярных выражений Groovy для средства сопоставления (=~), которое будет // приведено к логическому значению: либо true, если значение содержит нашу строку, либо false в противном случае. def функциональный = языки . язык . findAll { это . парадигма =~ "функционал" } утверждать функционал . собирать его . _ name } == [ "Groovy" , "Ruby" ]
В Groovy строки можно интерполировать с помощью переменных и выражений с помощью GStrings: [32]
BigDecimal account = 10.0 def text = "Счет в настоящее время показывает баланс в размере $account" submit text == "Счет в настоящее время показывает баланс в размере 10,0"
GStrings, содержащие переменные и выражения, должны быть объявлены с использованием двойных кавычек.
Сложное выражение должно быть заключено в фигурные скобки. Это предотвращает интерпретацию его частей как принадлежащих окружающей строке, а не выражению:
BigDecimal minus = 4.0 text = "В настоящее время баланс счета составляет ${account - minus}" Assert text == "В настоящее время баланс счета составляет 6,0" // Без скобок, изолирующих выражение, это привело бы к следующему: text = "Счет в настоящее время показывает баланс $account - минус" утвержденный текст == "Счет показывает текущий баланс 10,0 - минус"
Вычисление выражения можно отложить, используя синтаксис стрелок:
BigDecimal Tax = 0,15 text = "На счете в настоящее время отображается баланс в размере ${->account - account*tax}" Tax = 0,10 // Значение налога было изменено ПОСЛЕ объявления GString. // Переменные выражения привязываются только тогда, когда выражение действительно необходимо вычислить :
Согласно собственной документации Groovy: «Когда компилятор Groovy компилирует сценарии и классы Groovy, в какой-то момент процесса исходный код будет представлен в памяти в виде конкретного синтаксического дерева, а затем преобразован в абстрактное синтаксическое дерево». Цель преобразований AST — предоставить разработчикам возможность подключиться к процессу компиляции, чтобы иметь возможность изменять AST до того, как он будет преобразован в байт-код, который будет выполняться JVM. Преобразования AST предоставляют Groovy улучшенные возможности метапрограммирования во время компиляции, обеспечивающие высокую гибкость. на уровне языка, без снижения производительности во время выполнения». [33]
Примеры AST в Groovy:
среди других.
Среда тестирования Spock использует преобразования AST, чтобы позволить программисту писать тесты с синтаксисом, не поддерживаемым Groovy, но затем соответствующий код обрабатывается в AST для получения допустимого кода. [34] Примером такого теста является:
def "максимум #a и #b равен #c" () { ожидаем: Math . Макс ( а , б ) == с где: а | б || с 3 | 5 || 5 7 | 0 || 7 0 | 0 || 0 }
Согласно документации Groovy, « Трейты — это структурная конструкция языка, которая позволяет: композицию поведений, реализацию интерфейсов во время выполнения, переопределение поведения и совместимость со статической проверкой/компиляцией типов».
Трейты можно рассматривать как интерфейсы , несущие как реализации по умолчанию, так и состояние. Признак определяется с помощью ключевого слова типажа:
типаж FlyingAbility { /* объявление признака */ String fly () { "Я летаю!" } /* объявление метода внутри типажа */ }
Затем его можно использовать как обычный интерфейс, используя ключевое слово implements
:
class Bird реализует FlyingAbility {} /* Добавляет признак FlyingAbility к возможностям класса Bird */ defbird = new Bird ( ) / * создает экземпляр нового Bird * / Assertbird . летать () == "Я лечу!" /* класс Bird автоматически получает поведение черты FlyingAbility */
Черты позволяют использовать широкий спектр способностей: от простого составления до тестирования.
Яркие примеры внедрения Groovy включают в себя:
Многие интегрированные среды разработки (IDE) и текстовые редакторы поддерживают Groovy:
Существует одна альтернативная реализация Groovy: