Groovy

Материал из Викиучебника — открытых книг для открытого мира
Перейти к: навигация, поиск

Первая программа[править]

Откройте редактор и напишите программу. По традиции, первая программа должна просто выводить приветствие миру:

println "Hello World!"

Сохраните её в файл helloworld.groovy и запустите.

groovy helloworld.groovy

На экране появится надпись:

Hello World!

На этом традиционную часть можно считать выполненной.

Комментарии и демонстрация результата[править]

Комментарием называется часть программного кода, пропускаемая при обработке (интерпретации или компиляции).

В Groovy знаком начала комментария служит //. Всё, что между ним и концом строки пропускается. Также комментарий можно расположить между /* и */. Пример:

println 2 + 2        // это комментарий
println "Привет!"    /* это тоже комментарий */
/* А это - многострочный 
комментарий */

Результат иллюстрируемого кода будет располагаться после последовательности //=>. Пример:

println 2 + 2       //=> 4
println "Привет"    //=> Привет

Переменные[править]

Переменная в Groovy определяется ключевым словом def (это аналогично типу Object в Java):

def a = 1 // объявление "безтиповой" переменной, присвоение ей значения типа int
a = "String" /* так как мы не указали тип при объявлении этой переменной, 
                то можно присваивать этой переменной значение другого типа */

int b = 2 // объявление переменной типа int
b = "String for type int?" // так как тип переменной указан, 
                           // то когда мы пытаемся присвоить String, получаем ошибку (GroovyCastException)

Строки[править]

В Groovy есть 2 типа строк:

  • Java Strings — строки в одинарных кавычках
  • Groovy Strings, известны как GStrings — в двойных кавычках; используя ${имя_переменной} можно "вставить" внутрь строки значение переменной
javaString = 'java' // Java String
groovyString = "Hello ${javaString}!" // GString

println javaString // => java
println groovyString // => Hello java!

bigGroovyString = """
  long
   long
    string
""" // Длинная строка с отступами

def a = "a"
println a
println a + "12" // конкатенация
println a * 3 // умножение

Списки[править]

Groovy содержит встроенную поддержку списков. Списки объявляются путем перечисления элементов в квадратных скобках:

def someList = [1,2,3,4]   // Создание списка
def emptyList = []         // Создание пустого списка

Обращение к элементам списка осуществляется по индексу, как к массиву в Java:

someList[0]                // Обращение к элементу по индексу

Новые элементы могут быть добавлены в список различными способами:

someList[5] = 6            // Добавление элемента в список
someList << 7 << 8         // Добавление элементов в конец списка
 
someList += [ 9, 10 ]      // "Приклеивание" списка

Теперь, совместное использование списков и ranges:

someList[1..3]                  // "Срезка" списка - получение подсписка
someList[2..4] = [ 12, 13, 14 ] // Изменение подсписка

Также, для индексирования можно использовать и списки:

someList[1,3,5..7,9]       // Получение подсписка

Также, Groovy предоставляет встроенный цикл, для перебора элементов списка, или элементов любого объекта, реализующего интерфейс java.lang.Iterable:

for ( e in someList ) {
	println e          // Распечатываем все элементы списка someList
}

Maps (Отображения)[править]

Аналогично спискам, Groovy поддерживает прозрачный синтаксис для работы с maps (ассоциативными массивами). Объявление выглядит следующим образом:

def someMap = [ 'a' : 1, 'b' : 2 ]   // Объявление

Также, существует специальный синтаксис для объявления пустых отображений:

// Объявление пустого отображения
def emptyMap = [:]                   
def otherEmptyMap = [:] as HashMap

Доступ к элементам осуществляется по ключу, с использованием оператора [], или же с ключом как полем:

someMap['a']                         // Доступ к элементу
someMap.a                            // Доступ к элементу как к полю

Аналогично производится и изменение элементов:

someMap['a'] = 2                     // Изменение элемент
someMap.a = 2                        // Изменение элемента, как поля

Для хранения обьектов в качестве ключа можно использовать скобки ():

def map = [(new String("username")):"james", nickname:"jcameron", (new Integer(22)):1234]
println map.get(new Integer(22))

Условное исполнение[править]

Одним из наиболее важных особенностей любого языка программирования является возможность выполнять различные коды в разных условиях.Простейший способ сделать это состоит в использовании IF конструкции. Например:

def amPM = Calendar.getInstance().get(Calendar.AM_PM)
if (amPM == Calendar.AM){
	println("Good morning")
} else {
	println("Good evening")
}

Не беспокойтесь о длинной первой строке, это просто какой-то код, чтобы определить, сейчас утро или вечер. Остальная часть кода выполняется следующим образом: сначала оценивается выражение в круглых скобках и в зависимости от результата true (истинно) или false (ложно) выполняется первый или второй блок кода. Смотрите ниже раздел логические выражения.

Обратите внимание, что ​​блок else не требуется, в отличии от первого блока:

amPM = Calendar.getInstance().get(Calendar.AM_PM)
if (amPM == Calendar.AM){
	println("Have another cup of coffee.")
}

Логические выражения[править]

Существует специальный тип данных в большинстве языков программирования, который используется для представления значений истинности,true (истина) и false (ложь). Простейшие логические выражения - это просто слова. Логические значения могут быть сохранены в переменных, как и любой другой тип данных:

def myBooleanVariable = true

Более сложные логические выражения использует один из булевых операторов:

  • ==
  •  !=
  • >
  • >=
  • <
  • <=

Большинство из них, довольно интуитивны. Оператор равенства ==, не путайте с оператором присваивания =. Оператор не равенство !=, то есть "не равно".

Некоторые примеры:

def titanicBoxOffice = 1234600000
def titanicDirector = "James Cameron"

def trueLiesBoxOffice = 219000000
def trueLiesDirector = "James Cameron"

def returnOfTheKingBoxOffice = 752200000
def returnOfTheKingDirector = "Peter Jackson"

def theTwoTowersBoxOffice = 581200000
def theTwoTowersDirector = "PeterJackson"

titanicBoxOffice > returnOfTheKingBoxOffice // вычисляется как истина
titanicBoxOffice >= returnOfTheKingBoxOffice // вычисляется как истина
titanicBoxOffice >= titanicBoxOffice // вычисляется как истина
titanicBoxOffice > titanicBoxOffice // оценивается как ложное
titanicBoxOffice + trueLiesBoxOffice < returnOfTheKingBoxOffice + theTwoTowersBoxOffice // оценивается как ложное

titanicDirector > returnOfTheKingDirector // оценивается как ложное, потому что "J" находится перед "Р"
titanicDirector < returnOfTheKingDirector // вычисляется как истина
titanicDirector >= "James Cameron" // вычисляется как истина
titanicDirector == "James Cameron" // вычисляется как истина

Логические выражения особенно полезны при использовании совместно с if-конструкциями. Например:

if (titanicBoxOffice + trueLiesBoxOffice > returnOfTheKingBoxOffice + theTwoTowersBoxOffice){
	println(titanicDirector + " is a better director than " + returnOfTheKingDirector)
}

Особенно полезна проверка на наличие значения. К примеру, определён ли данный ключ в карте:

def suvMap = ["Acura MDX":"\$36,700", "Ford Explorer":"\$26,845"]
if (suvMap["Hummer H3"] != null){
 	println("A Hummer H3 will set you back "+suvMap["Hummer H3"]);
}

Вообще null используется для указания на отсутствие значения выражения или переменной.

Функции[править]

Функции и методы всегда возвращают, как результат, последнее выражение.

//класс пользователя
class Person{
	String first, last
}
//функции без типизированных параметров
def printInfo(first, second){
	println "first name: $first, second name: $second"
}
def printFirstName(user){
	println "user: $user.first"
}
//создаем обьект класса Person с параметрами
def tempPerson = new Person(first: 'Adam', last: 'Smith')
// вызов функции разными способами
printInfo tempPerson.first, tempPerson.last
printFirstName(tempPerson)

printFirstName tempPerson

//типизированный параметр

def functionA(String str){
	println str
}
functionA 'String'// все ок
functionA 1 // вернётся исключение

int functionB(int argB) {
    argB + 2
}
String functionC() {
    "Hello World"
}

println functionB(1)
def hw = functionC()
println hw

Closures[править]

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

Синтаксис замыкания : { [closureArguments->] statements }

def closureFunction = {a, b ->
    println a
    println b
}
 
closureFunction(1, 2)

В замыканиях по умолчанию присутствует переменная it и ссылается на первый параметр в замыкании:

def closureA = { it }
assert closureA()  == null
assert closureA(1) == 1

Вызов замыкания:

def c = { it, arg-> println "${it} ${arg}" }
c.call("A", "B") // первый тип вызова
c("C", "D") // второй тип вызова

Определение замыкания в классе и его вызов:

public class ClassWithClosure {
    private int member = 20;

    private String method()
    {
        return "hello";
    }

    def publicMethodWithClosure(String name_)
    {
        def localVar = member + 5;
        def localVar2 = "Parameter: ${name_}";
        
        return {
            println "${member} ${name_} ${localVar} ${localVar2} ${method()}"
        }
    }
}

ClassWithClosure sample = new ClassWithClosure();
def closureVar = sample.publicMethodWithClosure("Xavier");
closureVar();

Специальный класс Expando для создания динамических обьектов и методов(как замыкания) которые можно вызывать:

def player = new Expando()
player.name = "Alec"
player.greeting = { "Hello, my name is $name" }

println player.greeting()
player.name = "Max"
println player.greeting()

Работа с файлами[править]

def out= new File('File1.txt')
// если файл не существует, то создаем файл
if(!out.exists()) {
    out.createNewFile()
    out << 'aaa\nbbb\nccc' // пишем текст в файл
}

list= [] // создаем список для строк
out.eachLine { list<< it } // и заполняем его 
println list.size() // выводим размер списка строк
println out.text // выводим весь текст
out.write('\nnew string') // пишем текст в файл
out.eachLine { println it}
out.append('\nappend string') // добавляем текст в файл

//выводим информацию о файле
println out.name
println out.isAbsolute()
println out.path 
println out.parent
println out.absolutePath 
println out.absoluteFile.toString()
println out.canonicalPath
println out.canonicalFile.toString()
println out.toURI().toString()

// Создаем директории
def dir= new File('Directory1')
dir.mkdir() //make directory, if it doesn't already exist
def dir2= new File('Directory2/SubDir1')
dir2.mkdirs()

Работа со строками[править]

lst = /This is my new string./
println lst

def b = "abcde" // также строка представляет собой и список символов
println b[2] // напечатает c
println b[1..3] // напечатает bcd

println "reverse me".reverse()
println "This is the end, my only friend!".tokenize(' ').reverse().join(' ')

Classes and Objects[править]

Описание и создание класса(по умолчанию класс имеет тип доступа public, а переменные класса имеют тип доступа private, но компилятор groovy сам сделает для этих полей геттеры и сеттеры с доступом public):

class Human {
    String name
    int age
}

def man = new Human();
man.name = "Adam"
man.age = 25;

def woman = new Human(name: "Eva", age: 22 )

Для обновления поля или полей можно использовать такой подход:

man.with{
    name = "Adam"
    age = 34
}

Классы:

// Классы могут не иметь конструктора
class ClassWithoutConstructor { }

class Bird {

    static startDate = new Date()

    private name

    def getName() { name }

    def setName(name) { this.name = name }

    static getStartDate() {
        startDate
    }
}

println Bird.startDate
def myBird = new Bird()
myBird.name = "Chack"
println myBird.name
// и иметь разные правила для генерации методов для полей
class Cat {

    def startDate = new Date() // getter и setter
    final String name = "Empty"
    final age      // только getter

    Cat(date, name) {
        startDate = date
        this.name = name
        this.age = 1
    }
}
Cat catty = new Cat(new Date(), "Pussy Cat")
println catty.name
println catty.age
println catty.startDate

Интерфейсы:

// Обьявление интерфейса
interface Voice{
    void voice();
    String getAnimalName();
}
// его реализация
class Dog implements Voice{

    @Override
    void voice() {
        println "Gav"
    }

    @Override
    String getAnimalName() {
        return Dog.class.getSimpleName()
    }

    void goMethod(){

    }
}
// и использование
def dog = new Dog() as Voice

// или можно привести к интерфейсу таким способом:
def dog = new Dog().asType(Voice.class)

dog.voice()
println "my name is: " + dog.getAnimalName()

// Реализация интерфейса как замыкания
def mainRunnable = {
    run:{
        try {
            int i = 5
            while (i>0) {
                sleep(1000)
                println "${i--}"
            }
        } catch (InterruptedException ex) {
            // error
        }
    }
} as Runnable

new Thread(mainRunnable).start()

Наследование:

class PersonA implements Comparable {
    def firstname, initial, surname
    PersonA(f,i,s) { firstname = f; initial = i; surname = s }
    int compareTo(other) { firstname <=> other.firstname }
}
def a = new PersonA('James', 'T', 'Kirk')
def b = new PersonA('Samuel', 'L', 'Jackson')
println a <=> b
// => -1

class PersonB extends PersonA {
    PersonB(f,i,s) { super(f,i,s) }
    int compareTo(other) { initial <=> other.initial }
}

a = new PersonB('James', 'T', 'Kirk')
b = new PersonB('Samuel', 'L', 'Jackson')
println a <=> b
// => 1

class Parent {
    private name // my child's name
    def setChildName(value) { name = value }
    def getChildName() { name }
}
class GrandParent extends Parent {
    private name // my grandchild's name
    def setgrandChildName(value) { name = value }
    def getGrandChildName() { name }
}
g = new GrandParent()
g.childName = 'Jason'
g.grandChildName = 'Rachel'
println g.childName       // => Jason
println g.grandChildName  // => Rachel

Абстрактные классы:

abstract class Shape {
    final name
    Shape(name) { this.name = name }
    abstract printName()
}

class Circle extends Shape {
    final radius
    Circle(radius) {
        super('circle')
        this.radius = radius
    }
    def area() { Math.PI * radius * radius }
    def printName() {
        println "I am a $name."
    }
}

class Rectangle extends Shape {
    final length, breadth
    def Rectangle(length, breadth) {
        super("rectangle")
        this.length = length
        this.breadth = breadth
    }
    def printName() {
        println "I am a $name."
    }
}
shapes = [new Circle(4.2), new Rectangle(5, 7)]
shapes.each { shape -> shape.printName() }

Статические внутренние классы:

class OuterClass {
    static class StaticInnerClass {
        public int getAge(){
            int a = 35
        }
    }
}

OuterClass.StaticInnerClass myInstance = new OuterClass.StaticInnerClass()

println myInstance.getAge()

Анонимные внутренние классы:

new Thread([run: {
    try {
        int i = 5
        while (i>0) {
            sleep(1000)
            println "${i--}"
        }
    } catch (InterruptedException ex) {
        // error
    }
}] as Runnable).start();

Enum:

enum Color{
	RED, GREEN, BLUE
}
 
def redColor = "RED" as Color
Color blueColor = "BLUE"
println blueColor

Расширенные возможности[править]

Импорт и переопределение[править]

Импортируемые пакеты по умолчанию которые добавляются компилятором в каждый сценарий:

  • java.io.*
  • java.lang.*
  • java.math.BigDecimal
  • java.math.BigInteger
  • java.net.*
  • java.util.*
  • groovy.lang.*
  • groovy.util.*

Можно импортировать пакеты в статическом контексте, а также назначать алиасы/псевдонимы:

import static java.awt.Color.BLUE
import static Boolean.FALSE as F // назначаем алиас с именем F
import static Calendar.getInstance as now // назначаем алиас с именем now 
import static java.lang.Integer.*

println BLUE
// напечатает java.awt.Color[r=0,g=0,b=255]
println !F
// напечатает true

println now().time
// напечатает Fri Jun 08 06:37:20 EEST 2011
def a = parseInt("123")
println a
// напечатает 123
// представление списка как интерфейса Set
def contacts = ['a', 'b', 'c'] as Set
println contacts.size() // напечатает 3

// представление списка как интерфейса Map
def contacts = ['a':10, 'b':20, 'c':30] as Map
println contacts['a'] // напечатает 10

Работа с данными[править]

// множественное присвоение
def (a, b, c) = [1,2,5]
println c

def (int myint, String mystring) = [5, "hello"]
println mystring

//диапазоны значений
def list = [3, 'Some string' , new Date()]

println list[0]
println list[1]

def letters = 'a'..'z'
def numbers = 0..<10

println numbers.size()

// работа с замыканиями
3.times { println 'Hi'}
[1,2,3].each {it -> println it}
(10..1).each {println it}
[ 'a' : 1, 'b' : 2 ].each {key, value -> println key}

Дата и время[править]

import java.util.GregorianCalendar as D
import static java.util.Calendar.getInstance as now

println new D(2011, 11, 25).time
println now().time
println new Date() + 1

dateStr = "2011-06-03"
date = Date.parse("yyyy-MM-dd", dateStr)
println 'Date was '+date.format("MM/dd/yyyy")

Аннотации[править]

// Эта аннотация которая генерирует из данного класса синглетон
@Singleton(lazy=true)
class MySingleton {
    def getHello(){
        "hello world"
    }
}

println MySingleton.instance.hello

// Эта аннотация позволяет внедрить в класс Manager поведение метода из класса Employee 
class Employee {
    def doWork() { 'my work' }
}

class Manager {
    @Delegate
    Employee slave = new Employee()
}

def worker = new Manager()
println worker.doWork()

// либо можно сделать то же самое с помощью mixin
class Employee2 { def doWork() { 'my work' } }
class Manager2 {}

Manager2.mixin Employee2

println new Manager2().doWork()

// Аннотация @Immutable делает обьект этого класса неизменяемым
// свойства обьект становятся readonly
@Immutable
class Person{
	String first, last
}

Regular Expressions[править]

Оператор Pattern[править]

Оператор pattern (~) обеспечивает простой способ создать java.util.regex.Pattern.
Пример:

def p = ~/foo/
assert p instanceof Pattern

В основном оператор pattern используется со слеш-строками (строки обрамлённых слешами), тем не менее этот оператор может использоваться с любыми видами строк Groovy:

p = ~'foo'                             /*1*/                           
p = ~"foo"                             /*2*/                           
p = ~$/dollar/slashy $ string/$        /*3*/                           
p = ~"${pattern}"                      /*4*/
  1. использование строк в одинарных кавычках
  2. использование строк в двойных кавычках
  3. использование строки обрамлённой доллар-слешем, позволяет использовать слеш и доллар без их экранирования
  4. использование GString

Find operator[править]

Alternatively to building a pattern, you can directly use the find operator =~ to build a java.util.regex.Matcher instance:

def text = "some text to match"
def m = text =~ /match/                /*1*/                                    
assert m instanceof Matcher            /*2*/                                  
if (!m) {                              /*3*/                                  
    throw new RuntimeException("Oops, text not found!")
}
  1. =~ creates a matcher against the text variable, using the pattern on the right hand side
  2. the return type of =~ is a Matcher
  3. equivalent to calling if (!m.find())

Since a Matcher coerces to a boolean by calling its find method, the =~ operator is consistent with the simple use of Perl’s =~ operator, when it appears as a predicate (in if, while, etc.).

Match operator[править]

The match operator (==~) is a slight variation of the find operator, that does not return a Matcher but a boolean and requires a strict match of the input string:

m = text ==~ /match/                   /*1*/                                         
assert m instanceof Boolean            /*2*/                                     
if (m) {                               /*3*/                                       
    throw new RuntimeException("Should not reach that point!")
}
  1. ==~ matches the subject with the regular expression, but match must be strict
  2. the return type of ==~ is therefore a boolean
  3. equivalent to calling if (text ==~ /match/)

The operators[править]

Capture groups[править]

Non-matching Groups[править]

Replacement[править]

Reluctant Operators[править]

Groovy SQL[править]

Подключение к базе данных и запрос SELECT(необходимо подключить драйвер JDBC для MySQL или другой базы):

import groovy.sql.Sql

Sql sql = Sql.newInstance("jdbc:mysql://localhost:3306/testdatabase", "user", "password", "com.mysql.jdbc.Driver")
// создаем таблицу
sql.execute('''create table users (
    id int(11) not null primary key auto_increment,
    username varchar(255), 
    age int(11)
)''')

Выполнение простого sql-запроса[править]

Вставка новой записи:

def age = 25
def name = "Adam"
sql.execute("insert into users (username, age) values (${name}, ${age})")
// or
sql.executeInsert("insert into users (username, age) values (${name}, ${age})")

Выборка первой записи из результата запроса:

def rowFirst = sql.firstRow('select username, age from users')
println "Row: Name = ${rowFirst.username} and Age = ${rowFirst.age}"

Выборка всех записей:

sql.eachRow("select * from users"){ row -> println row.username }

Удаление записи:

int id = 2
sql.execute('delete from users where id = ?' , [id])

Обновление записи:

def newUsername = 'New Name'
int rowsAffected = sql.executeUpdate('update users set username = ? where id=2', [newUsername])
println "updated: ${rowsAffected}";

Выполнение более сложных запросов[править]

import groovy.sql.Sql
import groovy.sql.DataSet

DataSet users
Sql sql = Sql.newInstance("jdbc:mysql://localhost:3306/testdatabase", "user", "password", "com.mysql.jdbc.Driver")
// таблица users должна быть создана
users = sql.dataSet("USERS")
users.add(username: "James", age: 55) //вставка новой записи в таблицу

DataSet findedUsers = users.findAll() // получение всех записей и их вывод
findedUsers.each{ println it.username}

Добавление записей в транзакции:

import groovy.sql.Sql

DataSet users
Sql sql = Sql.newInstance("jdbc:mysql://localhost:3306/testdatabase", "user", "password", "com.mysql.jdbc.Driver")

// таблица users должна быть создана
users = sql.dataSet("USERS")

sql.withTransaction {
    users.add(username: "Alec", age: 25)
    users.add(username: "Alec 2", age: 25)
}

Блочное добавление записей:

import groovy.sql.Sql

Sql sql = Sql.newInstance("jdbc:mysql://localhost:3306/testdatabase", "user", "password", "com.mysql.jdbc.Driver")
def updateCounts = sql.withBatch('insert into users(username, age) values (?, ?)') { ps ->
    ps.addBatch("New Name", 22)      // varargs style
    ps.addBatch(["New Name", 18])     // list
    ps.addBatch(["New Name", 31])    
}

Call Procedure[править]

Вызов процедур:

import groovy.sql.Sql

Sql sql = Sql.newInstance("jdbc:mysql://localhost:3306/testdatabase", "user", "password", "com.mysql.jdbc.Driver")
sql.call("{? = call MyProcedure(?)}", [Sql.VARCHAR, 'Sam']) { name ->
    assert name == 'Adam'
}
// or with GString
def first = 'Sam'
sql.call("{$Sql.VARCHAR = call MyProcedure($first)}") { name ->
    assert name == 'Adam'
}

Прочие советы[править]

Работа с JSON[править]

import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import groovy.json.JsonOutput
// Создание JSON обьекта
def json = new JsonBuilder()
json.person {
    username "Guillaume"
    age 33
    pets "Hector", "Felix"
}

println json.toString() // вывод в строку полученного обьекта

// Парсинг строки в JSON формате
def someJSONString = '{"person":{"username":"Guillaume","age":33,"pets":["Hector","Felix"]}}'
println JsonOutput.prettyPrint(someJSONString) // форматированный вывод обьекта

def slurper = new JsonSlurper()
def doc = slurper.parseText(someJSONString)

println doc.person.username // вывод имени
doc.person.pets.each {println it} // вывод животных

Работа с XML[править]

Создание XML документа из обьекта:

import groovy.xml.MarkupBuilder

writer = new StringWriter()
builder = new MarkupBuilder(writer)
petsList = [ "Hector", "Felix"]
builder.person() {
        username("Guillaume")
        age("33")
        gender("female")
        pets(){
            for (e in petsList){pet(e)}
        }

}
println writer.toString()

Работа с HTML:

import groovy.xml.MarkupBuilder
def writer = new StringWriter()
def builder = new MarkupBuilder(writer)
builder.html() {
    head() {
        title("This is the title")
    }

    body() {
        div("class" : "main") {
            p("this is the body")
        }
    }
}

println writer.toString()

Парсинг XML документа

def xmlString = """
  <person>
  <username>Guillaume</username>
  <age>33</age>
  <gender>female</gender>
  <pets>
    <pet>Hector</pet>
    <pet>Felix</pet>
  </pets>
</person>
"""

def person = new XmlSlurper().parseText(xmlString)
println person.username
person.pets.pet.each {println "pet's name:"+it}

Потоки и асинхронная работа[править]

//эта аннотация скачивает и устанавливает библиотеку в classpath
@Grab(group='org.codehaus.gpars', module='gpars', version='0.12')
import static groovyx.gpars.GParsExecutorsPool.withPool

//запуск в новом потоке
Thread.start {
  println Thread.currentThread().getId()
}
//50 раз сделать асинхронную загрузку страницы(и распечатать ответ) 
//по url в отдельных потоках
int count = 50
withPool(count) {
	count.times {
		Closure callUrl = {"http://google.com".toURL().withReader {
			reader -> println reader.readLines()
			}}
		callUrl.callAsync();
		
	}
}

Ссылки[править]