Приховані особливості Groovy?


78

Здається, про Groovy забули в цій темі, тому я просто задам те саме питання для Groovy.

  • Спробуйте обмежити відповіді на Groovy core
  • Одна функція на відповідь
  • Наведіть приклад і короткий опис функції, а не просто посилання на документацію
  • Позначте об’єкт, використовуючи жирний заголовок як перший рядок

Дивитися також:

  1. Приховані можливості Python
  2. Приховані особливості Ruby
  3. Приховані можливості Perl
  4. Приховані можливості Java

Відповіді:


56

Використання оператора розширеної точки

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

Це ярлик для animals.collect { it.size() }.


Що означає третій рядок?
ripper234

7
З контексту це означає виклик методу size для кожного елемента масиву і повернення масиву результатів. Насправді досить круто :-)
Майкл Рутерфурд

39

Метод with дозволяє перетворити це:

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

в це

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

3
це приносить мені старі спогади про об'єкт pascal :-)
fortran

1
Враховуючи, що це Groovy, чи не буде більш типовим порівнянням між myObj1.value = 10(і т. Д.) Та останнім, оскільки вам не потрібно викликати метод встановлення?
Філіп

37

Використання хешів як псевдооб’єктів.

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

У поєднанні з набором качок ви можете пройти довгий шлях із таким підходом. Навіть не потрібно видавати оператора "як".


2
нове для Groovy - це справді дуже приємно.
Стів Б.

37

Хтось знає про Елвіса ?

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)

1
це те саме, що нульовий оператор злиття (??) із C #?
Олексій Бараноський,

Здавалося б, так, хоча мені довелося шукати C # op.
billjamesdev

Не зовсім так, це скорочений потрійний оператор. Я написав про це: colinharrington.net/blog/2008/10/groovy-elvis-operator Ви також можете робити там повний вираз :-)
Колін Гаррінгтон,

Код, розміщений у відповіді, не компілюється, оскільки ключове слово "за замовчуванням" використовується як змінна. Натомість використовуйте "d", щоб зробити компіляцію коду.
Vorg van Geir

2
Немає жодної важливої ​​причини, лише дотримуючись конвенції, запропонованої ОП. На той час я не брав до уваги освіжаючий ефект, який спричинить моя дія.
gotomanners

35

З’ясувати, які методи існують на об’єкті, так само просто, як запитати metaClass:

"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"]

1
Спочатку це здається безглуздо. Але це неймовірно корисно. У python у вас є вбудована функція dir: dir ("foo") надає всі методи для рядка.
santiagobasulto

28

Для перехоплення відсутніх статичних методів використовуйте наступне

 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, у неї виникають труднощі при аналізі цього.
ripper234

3
Object Foo не має визначеного статичного методу з назвою B. Тим не менш, ви можете реалізувати його на льоту, додавши метод, який називається "$ static_methodMissing (String, Object)" і реалізуючи там все, що завгодно. Цей магічний метод викликається щоразу, коли викликається статичний метод, а об’єкт не має такого статичного методу.
Jen S.

24

Деструктурування

Це могло б називатися по-іншому в Groovy; це називається деструктуризацією в кложурі. Ви ніколи не повірите, наскільки це зручно.

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

Це називається багаторазовим призначенням у Groovy. Вікіпедія називає це паралельним призначенням .
Frank Kusters

Деструктуризація в Clojure набагато потужніша, ніж багаторазове призначення. clojure.org/…
Джейсон,

21

Для тестування Java-коду за допомогою groovy приголомшливий конструктор графіків об’єктів:

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, які є Listзначенням порожнього списку за замовчуванням, а не nullдля того, щоб працювати підтримка конструктора .)


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

Ах, краса багаторядкових струн. Кожна мова повинна їх прийняти.
ripper234

2
Не впевнений, чому багаторядковий рядок потребує "" "як роздільник, коли" міг бути розширений, щоб дозволити багаторядкові, а також однорядкові рядки.
Vorg van Geir

2
@VorgvanGeir за допомогою "" "означає, що вам не доведеться рятуватися".
undefined

1
@Brian Правда, але "" "a \ bc" de "f \ g" "" не компілюється, оскільки вам потрібно уникнути \ або \ g, і \ b діятиме як зворотний простір, якщо ви його не уникнете. Який сенс не потребувати втечі ", коли вам все одно потрібно врятуватися КОЖНОЮ спеціальною послідовністю всередині рядка?
Vorg van Geir

Тому що деякі з нас хочуть мати можливість писати "foo \ tbar". Але у Groovy також є: println (/ a \ bc "de" f \ g /) // -> a \ bc "de" f \ g
DarkStar

15

У 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"]

15

На відміну від Java, у Groovy в операторі switch можна використовувати що завгодно , а не лише примітивні типи. У типовій подіїВиконаний метод

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

15

Використання оператора космічного корабля

Мені подобається оператор Spaceship , корисний для всіляких сценаріїв нестандартного сортування. Деякі приклади використання наведено тут . Однією з ситуацій, коли це особливо корисно, є створення компаратора на льоту об’єкта за допомогою декількох полів. напр

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 ]

14

Закриття може змусити всі старі пробні ігри управління ресурсами зникнути. Потік файлів автоматично закривається в кінці блоку:

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

1
А також файловий маніпулятор належним чином закритий на випадок, коли під час закриття виникає виняток, мені це подобається навіть краще, ніж try-with-resources.
DarkStar

13

Функції, що надаються перетвореннями всередині groovy.transformпакета GDK , такі як:

  • @Immutable: Анотація @Immutable вказує компілятору виконати перетворення AST, яке додає необхідні геттери, конструктори, дорівнює, hashCode та інші допоміжні методи, які зазвичай пишуться при створенні незмінних класів із визначеними властивостями.
  • @CompileStatic: Це дозволить компілятору Groovy використовувати перевірку часу компіляції у стилі Java, а потім виконувати статичну компіляцію, тим самим минаючи протокол мета-об'єкта Groovy.
  • @Canonical: Анотація @Canonical вказує компілятору виконати перетворення AST, яке додає позиційні конструктори, дорівнює, hashCode та гарний принт toString до вашого класу.

Інші:

  • @Slf4jЦе локальне перетворення додає можливість реєстрації у вашій програмі за допомогою ведення журналу LogBack. Кожен виклик методу для незв’язаної змінної з іменем log буде відображено у виклику реєстратора.
  • XML Slurper Groovy : легкий синтаксичний аналіз XML. Вбивча особливість!

12

Ви можете перетворити список на карту за допомогою 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']

Це потрібно as Object []в першому рядку?
Каміль

12

Впровадження інтерфейсу на основі закриття

Якщо у вас є набране посилання, наприклад:

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

8

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

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

7

Я знаю, що я трохи запізнився, але, думаю, тут відсутні деякі приємні функції:

Оператори збору плюс / мінус

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


6

Підкреслення в літералах

Під час написання довгих буквальних чисел оку важче зрозуміти, як деякі числа згруповані, наприклад, з групами тисяч, слів і т. Д. Якщо дозволити розміщувати підкреслення в літералах чисел, легше виявити ці групи:

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

5

Ще один приємний аргумент - упорядкування за допомогою неявних аргументів.

Цей код:

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" - я залишив їх просто для ясності.


Я хотів би, щоб це було так, але наразі Groovy (1.6) підтримує лише іменовані аргументи для конструкторів об'єктів. Ви можете використовувати цей синтаксис для викликів методів, але він пакує будь-які іменовані аргументи в Map, а потім шукає foo (Map).
Коді Кастерлайн,

Я збентежений щодо того, що, на вашу думку, я сказав, що означає інше.
Роберт Фішер,

4

Я думаю, що це комбінація замикань як параметрів та значень параметра за замовчуванням:

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"


4

Використання оператора поширення в параметрах методу

Це чудова допомога при перетворенні коду в дані:

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('_'))

Ні, я маю на увазі перетворення коду в дані, створення масиву, який є даними, еквівалентний списку аргументів, який зазвичай є статичним. Але я розумію вашу думку, це залежить від вашої точки зору. Я розглядаю це з точки зору рефакторингу існуючого статичного коду до більш динамічного! C коду.
Luis Muñiz

Можливо, "при використанні дизайну, керованого даними"?
DarkStar

3

Мемоізація

Запам'ятовування - це техніка оптимізації, яка полягає у збереженні результатів дорогих викликів функцій і поверненні кешованого результату при кожному повторному виклику функції з тими ж аргументами.

Існує необмежена версія, яка буде кешувати коли-небудь пару (вхідні аргументи, повернене значення), яку вона коли-небудь побачить; та обмежена версія, яка буде кешувати останні побачені 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
}

2

Groovy може працювати багато, як Javascript. Ви можете мати приватні вари та функції через закриття. Ви також можете каррі функції за допомогою закриття.

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


2

Виклик динамічного методу

Ви можете викликати метод, використовуючи рядок із його назвою

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()'

2

Як побудувати дерево 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"}}}


2

Оператор безпечної навігації

Оператор безпечної навігації використовується, щоб уникнути 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

Кілька змінних уповільнення

1) Декларації кількох змінних в одному рядку

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

2) Використання різних оголошень типу.

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

0

Оператор примусу

Оператор примусу (as) - це варіант кастингу. Примус перетворює об'єкт з одного типу на інший, не будучи сумісними для призначення. Візьмемо приклад:

Ціле число x = 123
Рядок s = (Рядок) x
Ціле число не можна присвоїти рядку , тому воно створюватиме ClassCastException під час виконання Це можна виправити, використовуючи замість цього примус:

Integer x = 123 String s = x as String
Integer не може бути присвоєно рядку, але використання as примусить його до рядка

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.