Скрытые особенности Groovy?


похоже, что Groovy был забыт в этой теме, поэтому я просто задам тот же вопрос для Groovy.

  • попробуйте ограничить ответы на Groovy core
  • одна функция на ответ
  • приведите пример и краткое описание функции, а не просто ссылку на документацию
  • обозначьте функцию, используя жирный заголовок в качестве первой строки

Читайте также:

  1. скрытые возможности Python
  2. скрытые особенности Ruby
  3. скрытые возможности Perl
  4. скрытые возможности Java
30 78

30 ответов:

С помощью оператора spread-dot

def animals = ['ant', 'buffalo', 'canary', 'dog']
assert animals.size() == 4
assert animals*.size() == [3, 7, 6, 3]

это ярлык для animals.collect { it.size() }.

The С метод позволяет превратить этот:

 myObj1.setValue(10)
 otherObj.setTitle(myObj1.getName())
 myObj1.setMode(Obj1.MODE_NORMAL)

в этой

 myObj1.with {
    value = 10
    otherObj.title = name
    mode = MODE_NORMAL
 }

использование хэшей в качестве псевдо-объектов.

def x = [foo:1, bar:{-> println "Hello, world!"}]
x.foo
x.bar()

в сочетании с утиной печатью вы можете пройти долгий путь с этим подходом. Даже не нужно вытаскивать оператора "as".

кто-нибудь знает о Элвис?

def d = "hello";
def obj = null;

def obj2 = obj ?: d;   // sets obj2 to default
obj = "world"

def obj3 = obj ?: d;  // sets obj3 to obj (since it's non-null)

узнать, какие методы находятся на объекте, так же просто, как спросить метакласс:

"foo".metaClass.methods.name.sort().unique()

принты:

["charAt", "codePointAt", "codePointBefore", "codePointCount", "compareTo",
 "compareToIgnoreCase", "concat", "contains", "contentEquals", "copyValueOf", 
 "endsWith", "equals", "equalsIgnoreCase", "format", "getBytes", "getChars", 
 "getClass", "hashCode", "indexOf", "intern", "lastIndexOf", "length", "matches", 
 "notify", "notifyAll", "offsetByCodePoints", "regionMatches", "replace", 
 "replaceAll", "replaceFirst", "split", "startsWith", "subSequence", "substring", 
 "toCharArray", "toLowerCase", "toString", "toUpperCase", "trim", "valueOf", "wait"]

для перехвата отсутствующих статических методов используйте следующие

 Foo {
    static A() { println "I'm A"}

     static $static_methodMissing(String name, args) {
        println "Missing static $name"
     }
 }

Foo.A()  //prints "I'm A"
Foo.B()  //prints "Missing static B"

-Кен

Деструктурируется

Это может быть названо чем-то еще в Groovy; это называется деструктуризацией в clojure. Вы никогда не поверите, как это может пригодиться.

def list = [1, 'bla', false]
def (num, str, bool) = list
assert num == 1
assert str == 'bla'
assert !bool

для тестирования кода java с groovy, object graph builder является удивительным:

def company = builder.company( name: 'ACME' ) {
   address( id: 'a1', line1: '123 Groovy Rd', zip: 12345, state: 'JV' )
   employee(  name: 'Duke', employeeId: 1 ){
      address( refId: 'a1' )
   }
}

стандартная функция, но все равно очень хороший.

ObjectGraphBuilder

(вам нужно дать любые свойства вашего POJO, которые Lists значение по умолчанию пустого списка, а не null для поддержки строителя для работы.)

println 
"""
Groovy has "multi-line" strings.
Hooray!
"""

в groovy 1.6 регулярные выражения работают со всеми итераторами замыкания (например, каждый, собирать, вводить и т. д.) и позволяют легко работать с группами захвата:

def filePaths = """
/tmp/file.txt
/usr/bin/dummy.txt
"""

assert (filePaths =~ /(.*)\/(.*)/).collect { full, path, file -> 
        "$file -> $path"
    } ==  ["file.txt -> /tmp", "dummy.txt -> /usr/bin"]

В отличие от Java, в Groovy, все может быть использовано в переключатель заявление, а не только примитивные типы. В типичном eventPerformed метод

switch(event.source) {
   case object1:
        // do something
        break
   case object2:
        // do something
        break
}

С помощью оператора космического корабля

Мне нравится оператор космического корабля, полезно для всех видов пользовательских сценариев сортировки. Некоторые примеры использования здесь. Одна из ситуаций, в которой это особенно полезно, заключается в создании компаратора на лету объекта с использованием нескольких полей. например,

def list = [
    [ id:0, first: 'Michael', last: 'Smith', age: 23 ],
    [ id:1, first: 'John', last: 'Smith', age: 30 ],
    [ id:2, first: 'Michael', last: 'Smith', age: 15 ],    
    [ id:3, first: 'Michael', last: 'Jones', age: 15 ],   
]

// sort list by last name, then first name, then by descending age
assert (list.sort { a,b -> a.last <=> b.last ?: a.first <=> b.first ?: b.age <=> a.age })*.id == [ 3,1,0,2 ]

закрытие может сделать все старые попытки-наконец игры управления ресурсами уходят. Поток файлов автоматически закрывается в конце блока:

new File("/etc/profile").withReader { r ->
    System.out << r
}

функции, предоставляемые преобразованиями внутри ГДК , например:

  • @Immutable: аннотация @Immutable указывает компилятору выполнить преобразование AST, которое добавляет необходимые геттеры, конструкторы, equals, hashCode и другие вспомогательные методы, которые обычно записываются при создании неизменяемых классов с определенными свойствами.
  • @CompileStatic: это позволит использовать в Groovy компилятор проверки времени компиляции в стиле Java затем выполняют статическую компиляцию, таким образом обходя протокол Groovy meta object.
  • @Canonical: аннотация @Canonical указывает компилятору выполнить преобразование AST, которое добавляет позиционные конструкторы, equals, hashCode и довольно печатную строку toString в ваш класс.

другие:

  • @Slf4j это локальное преобразование добавляет возможность ведения журнала в вашу программу использование ведения журнала обратной связи. Каждый вызов метода для несвязанной переменной с именем log будет сопоставлен с вызовом регистратора.
  • Groovy с XML в Slurper: легкий разбор XML. Убийца особенность!

вы можете преобразовать список в карту с помощью toSpreadMap(), удобно в то время, когда порядок в списке достаточно, чтобы определить ключи и значения, связанные с ними. См. пример ниже.

def list = ['key', 'value', 'foo', 'bar'] as Object[]
def map = list.toSpreadMap()

assert 2 == map.size()
assert 'value' == map.key
assert 'bar' == map['foo']

Реализация Интерфейса На Основе Закрытия

Если у вас есть типизированная ссылка, например:

MyInterface foo

вы можете реализовать весь интерфейс через:

foo = {Object[] args -> println "This closure will be called by ALL methods"} as MyInterface

кроме того, если вы хотите реализовать каждый метод отдельно, вы можете использовать:

foo = [bar: {-> println "bar invoked"}, 
    baz: {param1 -> println "baz invoked with param $param1"}] as MyInterface

удалить null значения из списка

def list = [obj1, obj2, null, obj4, null, obj6]
list -= null
assert list == [obj1, obj2, obj4, obj6]

Я знаю, что немного опоздал, но я думаю, что здесь не хватает некоторых приятных функций:

коллекция плюс/минус операторов

def l = [1, 2, 3] + [4, 5, 6] - [2, 5] - 3 + (7..9)
assert l == [1, 4, 6, 7, 8, 9]

def m = [a: 1, b: 2] + [c: 3] - [a: 1]
assert m == [b: 2, c: 3]

переключатель

switch (42) {
  case 0: .. break
  case 1..9: .. break
  case Float: .. break
  case { it % 4 == 0 }: .. break
  case ~/\d+/: .. break
}

диапазоны и индексации

assert (1..10).step(2) == [1, 3, 5, 7, 9]
assert (1..10)[1, 4..8] == [2, 5, 6, 7, 8, 9]
assert ('a'..'g')[-4..-2] == ['d', 'e', 'f']

Unicode в именах переменных

def α = 123
def β = 456
def Ω = α * β
assert Ω == 56088

@ Delegate

class Foo {
    def footest() { return "footest"}   
}

class Bar {
    @Delegate Foo foo = new Foo()     
}

def bar = new Bar()

assert "footest" == bar.footest()

подчеркивание в литералах

при написании длинных буквенных номеров, это сложнее на глаз, чтобы выяснить, как некоторые цифры группируются вместе, например, с группами из тысячи слов, и т. д. Позволяя размещать подчеркивание в числовых литералах, легче определить эти группы:

long creditCardNumber = 1234_5678_9012_3456L
long socialSecurityNumbers = 999_99_9999L
double monetaryAmount = 12_345_132.12
long hexBytes = 0xFF_EC_DE_5E
long hexWords = 0xFFEC_DE5E
long maxLong = 0x7fff_ffff_ffff_ffffL
long alsoMaxLong = 9_223_372_036_854_775_807L
long bytes = 0b11010010_01101001_10010100_10010010

переупорядочение аргументов с неявными аргументами-еще один хороший.

этот код:

def foo(Map m=[:], String msg, int val, Closure c={}) {
  [...]
}

создает все эти различные методы:

foo("msg", 2, x:1, y:2)
foo(x:1, y:2, "blah", 2)
foo("blah", x:1, 2, y:2) { [...] }
foo("blah", 2) { [...] }

и многое другое. Невозможно испортить, поставив именованные и порядковые аргументы в неправильном порядке / позиции.

конечно, в определении "foo" вы можете оставить "String" и "int" из "String msg" и "int val" - я оставил их только для ясности.

Я думаю, что это комбинация замыканий в качестве параметра и параметра-значения по умолчанию:

public void buyItems(Collection list, Closure except={it > 0}){
  list.findAll(){except(it)}.each(){print it}
}
buyItems([1,2,3]){it > 2}
buyItems([0,1,2])

печатает: "312"

использование оператора spread в параметрах метода

Это отличная помощь при преобразовании кода в данные:

def exec(operand1,operand2,Closure op) {
    op.call(operand1,operand2)
}

def addition = {a,b->a+b}
def multiplication = {a,b->a*b}

def instructions = [
     [1,2,addition],
     [2,2,multiplication]
]

instructions.each{instr->
    println exec(*instr)
}

также полезно это использование:

String locale="en_GB"

//this invokes new Locale('en','GB')
def enGB=new Locale(*locale.split('_'))

Memoization

Memoization-это метод оптимизации, который заключается в хранении результатов дорогостоящих вызовов функций и возврате кэшированного результата всякий раз, когда функция вызывается снова с теми же аргументами.

существует неограниченная версия, которая будет кэшировать когда-либо пару (входные аргументы, возвращаемое значение), которую он когда-либо увидит; и ограниченная версия, которая будет кэшировать последние N входных аргументов и их результаты, используя LRU кэш.

запоминание методов:

import groovy.transform.Memoized

@Memoized
Number factorial(Number n) {
    n == 0 ? 1 : factorial(n - 1)
}

@Memoized(maxCacheSize=1000)
Map fooDetails(Foo foo) {
    // call expensive service here
}

мемоизация закрытия:

def factorial = {Number n ->
    n == 0 ? 1 : factorial(n - 1)
}.memoize()

fooDetails = {Foo foo ->
    // call expensive service here
}.memoizeAtMost(1000)

страница Википедии имеется обширная информация об использовании мемоизации в области компьютерных наук. Я просто укажу на одно простое практическое применение.

откладывание инициализации константы до последнего возможного момента

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

в этом случае вы можете преобразовать свою константу в геттер и украсить ее @Memoized. Он будет вычислен только один раз, при первом обращении к нему, а затем значение кэшируется и повторно используется:

import groovy.transform.Memoized

@Memoized
def getMY_CONSTANT() {
    // compute the constant value using any external services needed
}

Groovy может работать очень похоже на Javascript. Вы можете иметь частные vars и функции через закрытие. Вы также можете Карри функции с закрытиями.

class FunctionTests {

def privateAccessWithClosure = {

    def privVar = 'foo'

    def privateFunc = { x -> println "${privVar} ${x}"}

    return {x -> privateFunc(x) } 
}


def addTogether = { x, y ->
    return x + y
}

def curryAdd = { x ->
    return { y-> addTogether(x,y)}
}

public static void main(String[] args) {
    def test = new FunctionTests()

    test.privateAccessWithClosure()('bar')

    def curried = test.curryAdd(5)

    println curried(5)
}
}

выход:

foo bar 10

динамический вызов метода

вы можете вызвать метод, используя строку с его названием

class Dynamic {
    def one() { println "method one()" }
    def two() { println "method two()" }
}

def callMethod( obj, methodName ) {
    obj."$methodName"()
}

def dyn = new Dynamic()

callMethod( dyn, "one" )               //prints 'method one()'
callMethod( dyn, "two" )               //prints 'method two()'
dyn."one"()                            //prints 'method one()'

как построить дерево JSON в пару строк в groovy ?

1) определите свое дерево с самореферентных withDefault закрытие

def tree // declare  first before using a self reference
tree = { ->  [:].withDefault{ tree() } }

2) Создайте свое собственное дерево JSON

frameworks = tree()
frameworks.grails.language.name = 'groovy'
frameworks.node.language.name = 'js'

def result =  new groovy.json.JsonBuilder(frameworks)

что дает: {"grails":{"language":{"name":"groovy"}},"node":{"language":{"name":"js"}}}

оператор безопасной навигации

оператор безопасной навигации используется, чтобы избежать исключения NullPointerException. Обычно, когда у вас есть ссылка на объект, вам может потребоваться проверить, что он не является нулевым, прежде чем обращаться к методам или свойствам объекта. Чтобы избежать этого, оператор безопасной навигации просто вернет null вместо того, чтобы выдавать исключение, например:

def person = Person.find { it.id == 123 }        // find will return a null instance    
def name = person?.name                          // use of the null-safe operator prevents from a NullPointerException, result is null

несколько переменных торможения

1) объявления нескольких переменных в одной строке

def (a,b,c) = [1,2,3]

2) Использование различных типов объявлений.

def (String a, int b) = ['Groovy', 1]

оператор принуждения

оператор принуждения (as)является вариантом приведения. Принуждение преобразует объект из одного типа в другой без их совместимости для назначения. Давайте возьмем пример:

целое число x = 123
Строка s = (строка) x
Целое число не может быть присвоено строке, поэтому он будет производить ClassCastException во время выполнения Это может быть исправлено с помощью принуждения вместо:

целое число x = 123 Строка s = x as Строка
Целое число не присваивается строке, но использование as принудит его к строке