Практически Groovy : Развитие Groovy

Познакомьтесь с новым синтаксисом Groovy, совместимым с JSR

С выходом версии парсера, совместимой с JSR-241 (а также в последующих версиях) изменения в синтаксисе Groovy были формализованы - что означает, если раньше вы не уделяли этому внимания, то теперь самое время начать это делать. В этом месяце практикующий специалист по Groovy Эндрю Гловер (Andrew Glover) проходит по наиболее важным изменениям в синтаксисе Groovy и демонстрирует удобную функцию, которой нет в классическом Groovy.

Эндрю Гловер, президент компании, Stelligent Incorporated

Эндрю Гловер является президентом компании Stelligent Incorporated , которая помогает другим фирмам решать проблемы качества программного обеспечения. Для этого используются эффективные стратегии тестирования и технологии непрерывной интеграции, которые позволяют коллективам разработчиков постоянно контролировать качество кода, начиная с ранних стадий разработки. Просмотрите блог Энди , там можно найти список его публикаций.



01.11.2007

Прошел уже почти год с того дня, когда я познакомил вас с Groovy в статье "Feeling Groovy" в серии alt.lang.jre. С тех пор Groovy повзрослел, пройдя несколько версий, в которых последовательно решались проблемы реализации языка и удовлетворялись запросы на функциональность, поступающие от сообщества разработчиков. И, наконец, Groovy сделал гигантский шаг вперед в апреле этого года, с выходом официальной версии нового парсера, нацеленной на стандартизацию языка в части обработки JSR.

В выпуске Practically Groovy за этот месяц я отпраздную рост Groovy, представив наиболее важные изменения, формализуемые новым отличным парсером Groovy; а именно объявление переменных и closures. Поскольку я буду сравнивать новый синтаксис Groovy с классическим синтаксисом, который я описывал в первой статье о Groovy, возможно, вы захотите открыть в другом окне браузера статью "Feeling Groovy".

Зачем нужны изменения?

Если вы следили за Groovy некоторое время, либо читая статьи и блоги, либо разрабатывая код, вы могли заметить в языке пару небольших проблем. Когда дело доходит до сложных операций, например, навигации по объектам, и особенно closures, Groovy страдает от встречающейся временами двусмысленности и определенных ограничений синтаксиса. Несколько месяцев назад команда Groovy, в рамках процесса JSR, начала работу по решению этих проблем. В решении, представленном в апреле с выходом версии groovy-1.0-jsr-01, был обновлен синтаксис, а также был представлен парсер, "знающий" этот синтаксис, для его стандартизации.

Об этой серии руководств

Чтобы применять какой-либо инструмент в практике разработки, необходимо знать, как им пользоваться и когда его лучше отложить в сторону. Языки сценариев могут быть чрезвычайно полезным помощником, но только если применять их правильно и в подходящих ситуациях. В связи с этим в серии статей Practically Groovy мы рассматриваем практические применения Groovy и показываем, когда и как его можно успешно использовать.

Хорошая новость состоит в том, что в новом синтаксисе реализовано множество усовершенствований языка. Другая хорошая новость состоит в том, что он не сильно отличается от старого. Как и все в Groovy, синтаксис был разработан с расчетом на скорейшее изучение и максимальную отдачу.

Конечно же, с появлением нового совместимого с JSR парсера часть "классического" синтаксиса стала несовместимой с новым Groovy. Вы увидите это, если попробуете запустить примеры кода из предыдущих статей этой серии с новым парсером: Вполне возможно, что они не будут работать! Это может показаться чрезмерной строгостью - особенно для такого свободного языка, как Groovy, – но задача нового парсера состоит в том, чтобы обеспечить дальнейший рост Groovy как стандартизованного языка для платформы Java. Я думаю, это будет полезной экскурсией по новому Groovy.


Да, это по-прежнему Groovy!

Прежде чем вдаваться в подробное описание изменений, я потрачу несколько секунд на перечисление того, что осталось неизменным. Во-первых, сама природа динамической типизации не изменилась. Явная типизация переменных (то есть объявление переменной как String или Collection), остается необязательным. Скоро я объясню одно небольшое дополнение к этому правилу.

Многие испытают облегчение от того, что точки с запятой также остаются необязательными. Были аргументы "за" и "против" такой синтаксической мягкости, но в конце концов победило мнение "лучше меньше, да лучше". Подводя итог: Если хотите, вы по-прежнему можете использовать точки с запятой.

Коллекции также большей частью не претерпели изменений. Вы по-прежнему можете объявлять коллекции типа list, используя синтаксис array и map так же, как и раньше (то есть так, как вы научились из статьи "Feeling Groovy (EN)"). С другой стороны, диапазоны слегка изменились, и вскоре я покажу, как именно.

И, наконец, дополнения Groovy к стандартным классам JDK не изменились совсем. Синтаксис и интерфейсы API остались прежними, как и обычные типы Java File, о которых я расскажу позже.


Переменные переменные

Вероятно, с введением нового синтаксиса, совместимого с JSR, наибольший удар пришелся на долю правил работы с переменными в Groovy. Классический Groovy был очень гибким (и очень кратким) в отношении объявления переменных. В новом JSR Groovy перед каждой переменной должно указываться либо ключевое слово def либо модификатор, например private, protected или public. Конечно же, вы всегда можете указать также и тип переменной. Кроме того, если вы определяете класс и хотите объявить свойства (которые объявляются в стиле JavaBeans, с getter-ами и setter-ами), вы можете объявить поля, указав ключевое слово @Property. Внимание - буква P в слове Property заглавная!

Например, когда я знакомил вас с GroovyBeans в статье "Feeling Groovy" в листинге 22 этой статьи я объявил тип LavaLamp. Он больше не совместим с JSR, и если вы попытаетесь запустить парсер, он вернет ошибку. К счастью, выполнить перенос класса не так сложно: Все, что мне пришлось сделать - это добавить атрибут @Property ко всем нужным переменным, как видно из приведенного ниже листинга 1:

Листинг 1. Возвращение LavaLamp
package com.vanward.groovy

class LavaLamp{
  @Property model
  @Property baseColor
  @Property liquidColor
  @Property lavaColor
}

llamp = new LavaLamp(model:1341, baseColor:"Black", 
  liquidColor:"Clear", lavaColor:"Green")
println llamp.baseColor
println "Lava Lamp model ${llamp.model}"
myLamp = new LavaLamp()
myLamp.baseColor = "Silver"
myLamp.setLavaColor("Red")
println "My Lamp has a ${myLamp.baseColor} base"
println "My Lava is " + myLamp.getLavaColor()

Не так плохо, верно?

Как было описано выше, ключевое слово def необходимо для любой переменной, у которой нет иного модификатора, ключевого слова @Property или типа. Например, в коде, приведенном в листинге 2, в методе toString содержится ошибочно определенная переменная numstr, наличие которой приведет к возникновению ошибки при попытке запуска парсера JSR:

Листинг 2. Не забывайте про ключевое слово def!
class Person { 
  @Property fname
  @Property lname
  @Property age
  @Property address
  @Property contactNumbers
  String toString(){

   numstr = new StringBuffer()

   if (contactNumbers != null){
	   contactNumbers.each{
		numstr.append(it)
		numstr.append(" ")
	   }
   }

   "first name: " + fname + " last name: " + lname + 
	" age: " + age + " address: " + address + 
	" contact numbers: " + numstr.toString()
 }
}

Узнаете этот код? Я позаимствовал его из листинга 1 статьи "Stir some Groovy into your Java apps (EN)." В листинге 3 вы можете увидеть сообщение об ошибке, которое появится в случае, если вы попытаетесь запустить код без изменений:

Листинг 3. Ага - отличное сообщение об ошибке!
c:\dev\projects>groovy BusinessObjects.groovy

 BusinessObjects.groovy: 13: The variable numstr is undefined in the current scope
 @ line 13, column 4.
      numstr = new StringBuffer()
      ^
1 Error

Решение, конечно же, состоит в добавлении ключевого слова def к numstr в методе toString. Это простое решение показано в листинге 4.

Листинг 4. Версия с def
  String toString(){

   def numstr = new StringBuffer()

   if (contactNumbers != null){
	   contactNumbers.each{

		numstr.append(it)
		numstr.append(" ")
	   }
   }

   "first name: " + fname + " last name: " + lname + 
	" age: " + age + " address: " + address + 
	" contact numbers: " + numstr.toString()
 }

Также я мог указать модификатор numstr, например private, или объявить его как StringBuffer. В любом случае, в JSR Groovy я должен указать перед названием переменной хотя бы что-нибудь.


Сближение с closures

Синтаксис closures изменился, но большей частью только в отношении параметров. Если в классическом Groovy вы объявляли параметр closure, в качестве разделителя надо было использовать символ |. Как вы, вероятно, знаете, символ | также является побитовым оператором в обычном языке Java; поэтому символ | нельзя было использовать в Groovy, за исключением контекста объявления параметров closure.

Синтаксис задания параметров в классическом Groovy вы можете увидеть в листинге 21 статьи "Feeling Groovy", где я демонстрировал итерации. Как вы помните, я использовал метод find, который пытался найти значение 3. Я передавал параметр x, который представляет следующее значение iterator (опытные разработчики Groovy отметят, что x совершенно необязателен и я мог бы сослаться напрямую на переменную it). В JSR Groovy я должен опустить | и заменить его Nice-подобным разделителем ->, как показано ниже в листинге 5:

Листинг 5. Новый синтаксис closure Groovy
[2, 4, 6, 8, 3].find { x ->
  if (x == 3){
    println "found ${x}"
  }
}

Не напоминает ли вам новый синтаксис closure синтаксис блоков в языке Nice? Если вы не знакомы с языком Nice, прочтите статью Twice as Nice (EN) и другие мои работы в серии alt.lang.jre.

Как я упоминал выше, JDK Groovy не изменился. Однако, как вы только что узнали, синтаксис closures изменился; таким образом, способ использования API в JDK Groovy также слегка изменился. В листинге 6 вы можете увидеть, как изменения влияют на Groovy IO – почти никак:

Листинг 6. JDK Groovy по-прежнему эффективен!
import java.io.File

new File("maven.xml").eachLine{ line ->
  println "read the following line -> " + line
}

Переработка фильтров

Теперь, хотя я и не люблю долгих предисловий, вспомните, как я в статье "Ant Scripting with Groovy" говорил о возможностях и путях использования closures. К счастью, большую часть примеров, которые я приводил в статьях этой серии, легко переделать под новый синтаксис. В листинге 7 я просто добавляю атрибут @Property к переменной strategy из Filter, оригинальный вариант которого показан в листингах 2 и 3 вышеупомянутой статьи. После этого я добавляю к closures разделитель -> и вуаля - все работает!

Листинг 7. Фильтрация исправлена!
package com.vanward.groovy

class Filter{
 @Property strategy
 boolean apply(str){
  return strategy.call(str)
 }
}

simplefilter = { str -> 
  if(str.indexOf("java.") >= 0){
    return true
  }else{
    return false
  }
}

fltr = new Filter(strategy:simplefilter)
assert !fltr.apply("test")
assert fltr.apply("java.lang.String")

rfilter = { istr ->
  if(istr =~ "com.vanward.*"){
    return true
  }else{
    return false
  }
}

rfltr = new Filter(strategy:rfilter)
assert !rfltr.apply("java.lang.String")
assert rfltr.apply("com.vanward.sedona.package")

Пока все хорошо, вы не находите? Понять новый синтаксис Groovy очень просто!


Изменения в диапазонах

Синтаксис диапазонов Groovy изменился еще меньше. Для обозначения исключений (верхней границы) в классическом Groovy достаточно было синтаксиса .... В JSR Groovy нужно просто отбросить последнюю точку (.) и заменить ее понятным символом <.

Посмотрите в приведенном ниже листинге 8, как я переработал пример с диапазонами из статьи "Feeling Groovy":

Листинг 8. Новый синтаксис диапазонов
myRange = 29..<32
myInclusiveRange = 2..5
println myRange.size() // выводит 3
println myRange[0]   // выводит 29
println myRange.contains(32) //  выводит false
println myInclusiveRange.contains(5) // выводит true

Вы скажете, что это неоднозначно?

Работая с Groovy, вы могли отметить хитрую функцию, которая позволяла вам получать ссылку на метод и по желанию обращаться по этой ссылке. Рассматривайте указатель на метод как удобный механизм быстрого вызова методов в сети объектов. Одним из интересных моментов, касающихся указателей на метод, является то, что их использование может означать, что код нарушает «закон Деметры».

"Какой еще закон Деметры?" - спросите вы. В соответствии с девизом разговаривай только с близкими друзьями, закон Деметры гласит, что мы должны избегать вызова методов объекта, который был возвращен методом другого объекта. Например, если объект Foo объявляет тип объекта Bar, клиенты могут получить доступ к Bar черезFoo. В результате может получиться довольно хрупкий код, поскольку изменение одного объекта отразится на всех остальных объектах сети.

Мой уважаемый коллега написал замечательную статью "The Paperboy, the Wallet, and the Law of Demeter" ("Продавец газет, кошелёк и закон Деметры") (см. раздел Ресурсы). Примеры в этой статье написаны на языке Java, а я переписал их ниже с помощью Groovy. В листинге 9 вы можете видеть, как этот код иллюстрирует закон Деметры - и как с его помощью можно опустошать чужие кошельки!

Листинг 9. Закон Деметры в действии (тсс!)
package com.vanward.groovy

import java.math.BigDecimal

class Customer {
 @Property firstName
 @Property lastName
 @Property wallet
}

class Wallet {
 @Property value;
 def getTotalMoney() {
  return value;
 }

 def setTotalMoney(newValue) {
  value = newValue;
 }
 def addMoney(deposit) {
  value = value.add(deposit)
 }
 def subtractMoney(debit) {
  value = value.subtract(debit)
 }
}

В листинге 9 определено два типа - Customer и Wallet. Обратите внимание, как тип Customer объявляет собственный экземпляр wallet. Как отмечалось ранее, простодушие кода вызывает проблемы. Например, что если я (как это сделал автор оригинальной статьи) добавлю злоумышленника - продавца газет - чтобы опустошать кошельки доверчивых клиентов? Для реализации своего гнусного замысла я использую указатели на метод Groovy, как можно видеть в листинге 10. Обратите внимание, как я смог получить ссылку на метод subtractMoney через экземпляр Customer с помощью нового синтаксиса Groovy & для указателей на методы.

Листинг 10. Появление продавца газет...
iwallet = new Wallet(value:new BigDecimal(32))
victim = new Customer(firstName:"Lane", lastName:"Meyer", wallet:iwallet)
victim.getWallet().subtractMoney(new BigDecimal("0.10"))//
Не нужно просить десять центов. Два доллара.
//газетчик становится злоумышленником, перехватывая ссылку на метод subtractMoney
mymoney = victim.wallet.&subtractMoney
mymoney(new BigDecimal(2)) // "Дайте мне мои два доллара!"
mymoney(new BigDecimal(25)) // "Штраф за задержку!"

Только поймите меня правильно: указатели на методы - это не средство взламывания кода или получения ссылок на чужие деньги! Это просто удобный вспомогательный механизм. Указатели на методы – это отличный повод вспомнить ваши любимые фильмы 80-х годов. Но если эти белые и пушистые штучки намокнут, вам никто не поможет! Говоря со всей серьезностью, можно рассматривать ссылку Groovy println как неявный указатель на метод System.out.println.

Если вы были внимательны, вы могли заметить, что в JSR Groovy мне приходится использовать новый синтаксис & для создания указателя на метод subtractMoney. Это дополнение, как вы, вероятно, догадываетесь, устраняет неопределенности классического Groovy.


Кроме того, здесь есть ещё кое-что новое!

Было бы неинтересно, если бы в версии JSR Groovy не было бы ничего нового, не так ли? К счастью, в JSR Groovy введено ключевое слово as, которое представляет собой механизм быстрого вызова. Эта возможность идет рука об руку с новым синтаксисом создания объектов, что облегчает создание в Groovy неспециальных классов с синтаксисом, подобным массиву. Под неспециальными я понимаю классы, которые содержатся в JDK, например, Color, Point, File и т.п.

В листинге 11 я с помощью нового синтаксиса создаю несколько простых типов:

Листинг 11. Нет новых функций в Groovy? если бы!
def nfile = ["c:/dev", "newfile.txt"] as File
def val = ["http", "www.vanwardtechnologies.com", "/"] as URL
def ival = ["89.90"] as BigDecimal
println ival as Float

Обратите внимание, что я создал новый File и URL, а также BigDecimal с помощью короткого синтаксиса, а также на то, как я смог преобразовать тип BigDecimal в Float с помощью as.


Что дальше?

Процесс формализации JSR для Groovy еще далек от завершения, особенно учитывая то, что в текущей версии Groovy (на момент публикации groovy-jsr-02) некоторые вещи все еще не работают. Например, в новом Groovy нельзя использовать циклы do/while. Кроме того, в Groovy еще не полностью реализован подход к созданию циклов for Java 5.0. В результате этого вы можете использовать синтаксис in, но не новый синтаксис :.

Эти функции слишком важны, чтобы обходиться без них, но не волнуйтесь - команда Groovy работает не покладая рук, чтобы реализовать их в ближайшие месяцы. В разделе Ресурсы можно найти ссылки для загрузки последней версии и получения информации о процессе JSR Groovy. Также обратите внимание на выпуск Practically Groovy в следующем месяце, где я (и два моих гостя) более подробно расскажем о closures в Groovy.

Ресурсы

  • Примите участие в обсуждении материала на форуме.
  • Оригинал статьи "Practically Groovy: Groovy's growth spurt (EN)".
  • Большая часть примеров в этой статье являются переработанными версиями исходных примеров на классическом Groovy, которые можно найти во вводной статье по языку Groovy "Feeling Groovy (EN)" (developerWorks, август 2004 г.).
  • Класс Filter, использованный для демонстрации изменений в closures в Groovy, впервые был разработан для статьи Эндрю Гловера "Ant Scripting with Groovy" (developerWorks, декабрь 2004 г.).
  • Загрузите Groovy и узнайте больше о процессе JSR Groovy с Codehaus - домашней страницы Groovy (EN).
  • alt.lang.jre (EN) - это серия статей, знакомящая читателей developerWorks с альтернативными языками для платформы Java.(EN)
  • Познакомьтесь с выпуском колонки Эндрю Гловера alt.lang.jre, "Twice as Nice (EN)" (developerWorks, октябрь 2004 г.), чтобы узнать больше о языке Nice.(EN)
  • Статьи по всем аспектам программирования на Java можно найти на сайте developerWorks, в разделе Java technology zone .
  • На сайте New to Java technology можно найти актуальные ресурсы, которые помогут вам начать программирование на Java.
  • Примите участие в жизни сообщества developerWorks, участвуя в блогах developerWorks (EN).

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=266189
ArticleTitle=Практически Groovy : Развитие Groovy
publish-date=11012007