Versteckte Features von Groovy?


78

Es scheint, als wäre Groovy in diesem Thread vergessen worden, also werde ich Groovy dieselbe Frage stellen.

  • Versuchen Sie, die Antworten auf den Groovy-Kern zu beschränken
  • Eine Funktion pro Antwort
  • Geben Sie ein Beispiel und eine kurze Beschreibung der Funktion, nicht nur einen Link zur Dokumentation
  • Beschriften Sie das Feature mit fettem Titel als erste Zeile

Siehe auch:

  1. Versteckte Funktionen von Python
  2. Versteckte Funktionen von Ruby
  3. Versteckte Funktionen von Perl
  4. Versteckte Funktionen von Java

Antworten:


56

Verwenden des Spread-Dot-Operators

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

Dies ist eine Verknüpfung für animals.collect { it.size() }.


Was bedeutet die dritte Zeile?
Ripper234

7
Aus dem Kontext bedeutet dies, dass Sie die Größenmethode für jedes Array-Element aufrufen und ein Array der Ergebnisse zurückgeben. Eigentlich ziemlich cool :-)
Michael Rutherfurd

39

Mit der with- Methode können Sie Folgendes ändern:

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

das mögen

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

3
das bringt mir alte erinnerungen über object pascal :-)
fortran

1
Wenn dies Groovy ist, wäre ein typischerer Vergleich nicht zwischen myObj1.value = 10(usw.) und letzterem, da Sie keine Setter-Methode aufrufen müssen?
Philip

37

Verwenden von Hashes als Pseudoobjekte.

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

In Kombination mit der Eingabe von Enten können Sie mit diesem Ansatz einen langen Weg gehen. Sie müssen nicht einmal den "as" -Operator auspeitschen.


2
neu bei Groovy - das ist wirklich sehr schön.
Steve B.

37

Kennt jemand Elvis ?

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
Ist dies dasselbe wie der Null-Koaleszenzoperator (??) von C #?
Alex Baranosky

Es scheint so, ja, obwohl ich das C # op nachschlagen musste.
Billjamesdev

Nicht genau, es ist ein verkürzter ternärer Operator. Ich habe es geschrieben: colinharrington.net/blog/2008/10/groovy-elvis-operator Sie können dort auch vollständige Ausdrücke machen :-)
Colin Harrington

Der in der Antwort angegebene Code wird nicht kompiliert, da das Schlüsselwort "default" als Variable verwendet wird. Verwenden Sie stattdessen "d", um den Code kompilieren zu lassen.
Vorg van Geir

2
Kein wichtiger Grund, nur im Einklang mit der vom OP vorgeschlagenen Konvention. Zu der Zeit habe ich den erfrischenden Effekt meiner Aktion nicht berücksichtigt.
Gotomanners

35

Das Herausfinden der Methoden für ein Objekt ist so einfach wie das Fragen der MetaClass:

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

Drucke:

["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
Das scheint zunächst albern. Aber es ist unglaublich nützlich. In Python haben Sie die integrierte dir-Funktion: dir ("foo") gibt alle Methoden für einen String an.
Santiagobasulto

28

Verwenden Sie Folgendes, um fehlende statische Methoden abzufangen

 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"

- Ken


Neu bei Groovy, mit einigen Schwierigkeiten beim Parsen.
Ripper234

3
Für das Objekt Foo ist keine statische Methode mit dem Namen B definiert. Sie können jedoch eine Methode im laufenden Betrieb implementieren, indem Sie eine Methode namens "$ static_methodMissing (String, Object)" hinzufügen und dort implementieren, was Sie möchten. Diese magische Methode wird immer dann aufgerufen, wenn eine statische Methode aufgerufen wird und für das Objekt diese statische Methode nicht definiert ist.
Jen S.

24

Destrukturierung

Es könnte in Groovy etwas anderes genannt werden; es heißt destructuring in clojure. Sie werden nie glauben, wie praktisch es sein kann.

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

In Groovy heißt es Mehrfachzuweisung . Wikipedia nennt es parallele Zuordnung .
Frank Kusters

Die Destrukturierung in Clojure ist viel leistungsfähiger als die Mehrfachzuweisung. clojure.org/…
Jason

21

Zum Testen von Java-Code mit Groovy ist der Object Graph Builder erstaunlich:

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

Standardausstattung, aber trotzdem sehr schön.

ObjectGraphBuilder

(Sie müssen alle Eigenschaften Ihres POJO angeben, die ein ListStandardwert einer leeren Liste sind, anstatt nulldass die Builder-Unterstützung funktioniert.)


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

Ah, die Schönheit von mehrzeiligen Saiten. Jede Sprache sollte diese übernehmen.
Ripper234

2
Nicht sicher, warum eine mehrzeilige Zeichenfolge "" als Trennzeichen benötigt, wenn "hätte erweitert werden können, um sowohl mehrzeilige als auch einzeilige Zeichenfolgen zuzulassen.
Vorg van Geir

2
@VorgvanGeir mit "" bedeutet, dass Sie nicht entkommen müssen ".
undefiniert

1
@Brian True, aber "" a \ bc "de" f \ g "" "wird nicht kompiliert, da Sie dem \ oder \ g entkommen müssen, und das \ b verhält sich wie eine Rücktaste, es sei denn, Sie entkommen ihm. Was
bringt es,

Weil einige von uns "foo \ tbar" schreiben wollen. Aber Groovy hat auch: println (/ a \ bc "de" f \ g /) // -> a \ bc "de" f \ g
DarkStar

15

In Groovy 1.6 funktionieren reguläre Ausdrücke mit allen Abschlussiteratoren (wie jedem, Sammeln, Injizieren usw.) und ermöglichen Ihnen die einfache Arbeit mit den Erfassungsgruppen:

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

Im Gegensatz zu Java kann in Groovy alles in einer switch- Anweisung verwendet werden, nicht nur primitive Typen. In einer typischen eventPerformed- Methode

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

15

Verwenden des Raumschiff-Operators

Ich mag den Raumschiff-Operator , der für alle Arten von benutzerdefinierten Sortierszenarien nützlich ist. Einige Anwendungsbeispiele finden Sie hier . Eine Situation, in der es besonders hilfreich ist, besteht darin, einen Komparator im laufenden Betrieb eines Objekts mithilfe mehrerer Felder zu erstellen. z.B

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

Durch das Schließen können alle alten Try-End-Spiele des Ressourcenmanagements verschwinden. Der Dateistream wird am Ende des Blocks automatisch geschlossen:

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

1
Und auch das Dateihandle ist ordnungsgemäß geschlossen, wenn innerhalb des Abschlusses eine Ausnahme ausgelöst wird. Dies gefällt mir sogar besser als das Ausprobieren von Ressourcen.
DarkStar

13

Die Funktionen, die durch die Transformationen im GDK- groovy.transformPaket bereitgestellt werden, wie z.

  • @Immutable: Die Annotation @Immutable weist den Compiler an, eine AST-Transformation auszuführen, die die erforderlichen Getter, Konstruktoren, Equals, HashCode und andere Hilfsmethoden hinzufügt, die normalerweise beim Erstellen unveränderlicher Klassen mit den definierten Eigenschaften geschrieben werden.
  • @CompileStatic: Dadurch kann der Groovy-Compiler Kompilierungszeitprüfungen im Java-Stil verwenden und dann eine statische Kompilierung durchführen, wodurch das Groovy-Metaobjektprotokoll umgangen wird.
  • @Canonical: Die Annotation @Canonical weist den Compiler an, eine AST-Transformation auszuführen, die Ihrer Klasse Positionskonstruktoren, Equals, HashCode und einen hübschen Druck toString hinzufügt.

Andere:

  • @Slf4jDiese lokale Transformation erweitert Ihr Programm um eine Protokollierungsfunktion mithilfe der LogBack-Protokollierung. Jeder Methodenaufruf für eine ungebundene Variable namens log wird einem Aufruf des Loggers zugeordnet.
  • Groovys XML Slurper : Einfaches Parsen von XML. Killer-Feature!

12

Sie können eine Liste mithilfe von toSpreadMap () in eine Karte konvertieren. Dies ist praktisch, wenn die Reihenfolge in der Liste ausreicht, um die Schlüssel und die damit verbundenen Werte zu bestimmen. Siehe Beispiel unten.

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

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

Ist das as Object []in der ersten Zeile notwendig?
Kamil

12

Implementierung einer schließungsbasierten Schnittstelle

Wenn Sie eine typisierte Referenz haben wie:

MyInterface foo

Sie können die gesamte Schnittstelle implementieren mit:

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

Wenn Sie jede Methode separat implementieren möchten, können Sie alternativ Folgendes verwenden:

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

8

nullWerte aus der Liste entfernen

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

7

Ich weiß, dass ich etwas spät dran bin, aber ich denke, hier fehlen einige nette Funktionen:

Sammlung Plus / Minus-Operatoren

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-Anweisung

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

Bereiche und Indizierung

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-Variablennamen

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

7

@Delegieren

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

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

def bar = new Bar()

assert "footest" == bar.footest()

6

In Literalen unterstreichen

Wenn Sie lange Literalzahlen schreiben, ist es für das Auge schwieriger herauszufinden, wie einige Zahlen zusammen gruppiert sind, z. B. mit Gruppen von Tausenden, Wörtern usw. Wenn Sie in Zahlenliteralen Unterstriche setzen können, ist es einfacher, diese Gruppen zu erkennen:

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

Die Neuordnung von Argumenten mit impliziten Argumenten ist eine weitere nette Sache.

Dieser Code:

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

Erstellt all diese verschiedenen Methoden:

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

Und mehr. Es ist unmöglich, Fehler zu machen, indem benannte und ordinale Argumente in die falsche Reihenfolge / Position gebracht werden.

Natürlich können Sie in der Definition von "foo" "String" und "int" von "String msg" und "int val" weglassen - ich habe sie nur aus Gründen der Klarheit belassen.


Ich wünschte, dies wäre der Fall, aber derzeit unterstützt Groovy (1.6) nur benannte Argumente für Objektkonstruktoren. Sie können diese Syntax für Methodenaufrufe verwenden, packt jedoch alle benannten Argumente in eine Map und sucht dann nach foo (Map).
Cody Casterline

Ich bin verwirrt darüber, was ich denke, dass ich gesagt habe, dass dies anders impliziert.
Robert Fischer

4

Ich denke, es ist eine Kombination von Abschlüssen als Parameter und Parameter-Standardwerte:

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

Drucke: "312"


4

Verwenden des Spread-Operators in Methodenparametern

Dies ist eine große Hilfe beim Konvertieren von Code in Daten:

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)
}

Hilfreich ist auch diese Verwendung:

String locale="en_GB"

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

Nein, ich meine, Code in Daten konvertieren und ein Array erstellen, bei dem es sich um Daten handelt, die einer Liste von Argumenten entsprechen, die normalerweise statisch sind. Aber ich verstehe Ihren Standpunkt, es hängt von Ihrer Perspektive ab. Ich betrachte es vom Standpunkt der Umgestaltung des vorhandenen statischen Codes zu dynamischerem Code.
Luis Muñiz

Vielleicht "bei Verwendung eines datengesteuerten Designs"?
DarkStar

3

Auswendiglernen

Memoization ist eine Optimierungstechnik, die darin besteht, die Ergebnisse teurer Funktionsaufrufe zu speichern und das zwischengespeicherte Ergebnis zurückzugeben, wenn die Funktion mit denselben Argumenten erneut aufgerufen wird.

Es gibt eine unbegrenzte Version, die jedes Paar (Eingabeargumente, Rückgabewert) zwischenspeichert, das jemals angezeigt wird. und eine eingeschränkte Version, die die letzten N Eingabeargumente und ihre Ergebnisse unter Verwendung eines LRU-Caches zwischenspeichert.

Auswendiglernen von Methoden:

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
}

Auswendiglernen von Schließungen:

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

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

Die Wikipedia-Seite enthält umfangreiche Informationen zur Verwendung von Memoization in der Informatik. Ich werde nur auf eine einfache praktische Anwendung hinweisen.

Verschieben der Initialisierung einer Konstanten auf den letztmöglichen Moment

Manchmal haben Sie einen konstanten Wert, der zum Zeitpunkt der Klassendefinition oder Erstellung nicht initialisiert werden kann. Beispielsweise kann der Konstantenausdruck eine andere Konstante oder eine Methode aus einer anderen Klasse verwenden, die nach der Initialisierung Ihrer Klasse durch etwas anderes (Spring oder ähnliches) eingefügt wird.

In diesem Fall können Sie Ihre Konstante in einen Getter umwandeln und damit dekorieren @Memoized. Es wird nur einmal berechnet, beim ersten Zugriff und dann im zwischengespeicherten und wiederverwendeten Wert:

import groovy.transform.Memoized

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

2

Groovy kann sehr ähnlich wie Javascript funktionieren. Sie können private Variablen und Funktionen über Closure haben. Sie können Funktionen auch mit Verschlüssen curry.

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)
}
}

Ausgabe:

foo bar 10


2

Dynamischer Methodenaufruf

Sie können eine Methode mit einer Zeichenfolge mit ihrem Namen aufrufen

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

Wie erstelle ich einen JSON-Baum in ein paar Zeilen in Groovy?

1) Definieren Sie Ihren Baum mit selbstreferenziellem withDefaultAbschluss

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

2) Erstellen Sie Ihren eigenen JSON-Baum

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

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

Welches gibt: {"grails":{"language":{"name":"groovy"}},"node":{"language":{"name":"js"}}}


2

Sicherer Navigationsbetreiber

Der Operator "Sichere Navigation" wird verwendet, um eine NullPointerException zu vermeiden. Wenn Sie einen Verweis auf ein Objekt haben, müssen Sie möglicherweise überprüfen, ob dieser nicht null ist, bevor Sie auf Methoden oder Eigenschaften des Objekts zugreifen. Um dies zu vermeiden, gibt der sichere Navigationsoperator einfach null zurück, anstatt eine Ausnahme auszulösen, wie folgt:

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

Verzögerung mehrerer Variablen

1) Mehrere Variablendeklarationen in einer Zeile

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

2) Verwenden verschiedener Typdeklarationen.

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

0

Zwangsoperator

Der Zwangsoperator (as) ist eine Variante des Gießens. Zwang konvertiert Objekte von einem Typ in einen anderen, ohne dass sie für die Zuweisung kompatibel sind. Nehmen wir ein Beispiel:

Integer x = 123
String s = (String) x
Integer kann keinem String zugewiesen werden , daher wird zur Laufzeit eine ClassCastException erzeugt. Dies kann stattdessen durch Zwang behoben werden:

Ganzzahl x = 123 Zeichenfolge s = x als Zeichenfolge
Ganzzahl kann keiner Zeichenfolge zugewiesen werden, aber die Verwendung von as zwingt sie zu einer Zeichenfolge

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.