78

似乎 Groovy 在这个线程中被遗忘了,所以我只会问 Groovy 相同的问题。

  • 尝试限制对 Groovy 核心的回答
  • 每个答案一个功能
  • 提供功能的示例和简短描述,而不仅仅是文档链接
  • 使用粗体标题作为第一行标记特征

也可以看看:

  1. Python的隐藏特性
  2. Ruby 的隐藏特性
  3. Perl 的隐藏特性
  4. Java的隐藏特性
4

31 回答 31

56

使用扩展点运算符

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

这是animals.collect { it.size() }.

于 2008-11-19T22:31:41.950 回答
39

with方法允许将其变为:

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

进入这个

 myObj1.with {
    value = 10
    otherObj.title = name
    mode = MODE_NORMAL
 }
于 2008-11-20T11:54:38.787 回答
37

使用哈希作为伪对象。

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

结合鸭子打字,你可以用这种方法走很长的路。甚至不需要抽出“as”运算符。

于 2008-11-19T22:07:37.660 回答
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)
于 2008-11-20T03:55:20.203 回答
35

找出对象上有哪些方法就像询问元类一样简单:

"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"]
于 2008-11-21T05:52:05.570 回答
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"

-

于 2010-08-31T01:28:12.327 回答
24

解构

它在 Groovy 中可能被称为其他东西;它在clojure中被称为解构。你永远不会相信它会有多方便。

def list = [1, 'bla', false]
def (num, str, bool) = list
assert num == 1
assert str == 'bla'
assert !bool
于 2012-10-04T14:10:03.367 回答
21

对于使用 groovy 测试 java 代码,对象图生成器非常棒:

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

标准功能,但仍然非常好。

对象图形构建器

(您确实需要提供 POJO 的任何属性,这些属性是List空列表的默认值,而不是null让构建器支持工作。)

于 2008-11-20T07:13:38.807 回答
19
println 
"""
Groovy has "multi-line" strings.
Hooray!
"""
于 2008-11-20T18:43:05.887 回答
15

与 Java 不同的是,在 Groovy 中,任何东西都可以在switch语句中使用,而不仅仅是原始类型。在一个典型的eventPerformed方法中

switch(event.source) {
   case object1:
        // do something
        break
   case object2:
        // do something
        break
}
于 2008-11-20T11:46:24.777 回答
15

在 groovy 1.6 中,正则表达式适用于所有闭包迭代器(如 each、collect、inject 等),并允许您轻松使用捕获组:

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

assert (filePaths =~ /(.*)\/(.*)/).collect { full, path, file -> 
        "$file -> $path"
    } ==  ["file.txt -> /tmp", "dummy.txt -> /usr/bin"]
于 2008-11-21T05:47:41.273 回答
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 ]
于 2011-08-09T17:19:09.053 回答
14

闭包可以让所有旧的资源管理的try-finally游戏消失。文件流在块结束时自动关闭:

new File("/etc/profile").withReader { r ->
    System.out << r
}
于 2008-11-20T18:56:02.960 回答
13

GDKgroovy.transform包内的转换提供的功能,例如:

  • @Immutable:@Immutable 注释指示编译器执行 AST 转换,该转换添加必要的 getter、构造函数、equals、hashCode 和其他通常在创建具有已定义属性的不可变类时编写的辅助方法。
  • @CompileStatic:这将让 Groovy 编译器使用 Java 风格的编译时检查,然后执行静态编译,从而绕过 Groovy 元对象协议。
  • @Canonical:@Canonical 注释指示编译器执行 AST 转换,将位置构造函数、equals、hashCode 和漂亮的打印 toString 添加到您的类。

其他:

  • @Slf4j此本地转换使用 LogBack 日志记录为您的程序添加了日志记录功能。对名为 log 的未绑定变量的每个方法调用都将映射到对记录器的调用。
  • Groovy 的 XML Slurper:轻松解析 XML。杀手级功能!
于 2013-06-28T17:38:42.227 回答
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
于 2009-06-15T13:59:39.077 回答
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']
于 2010-09-17T21:32:46.920 回答
8

从列表中删除null

def list = [obj1, obj2, null, obj4, null, obj6]
list -= null
assert list == [obj1, obj2, obj4, obj6]
于 2014-01-16T12:05:34.600 回答
7

@代表

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

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

def bar = new Bar()

assert "footest" == bar.footest()
于 2013-11-16T17:17:42.437 回答
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
于 2014-01-15T20:03:13.913 回答
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
于 2015-04-13T12:23:49.300 回答
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 msg" 和 "int val" 中去掉 "String" 和 "int" -- 我只是为了清楚起见把它们留在里面。

于 2008-11-19T22:53:47.397 回答
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”

于 2011-10-25T16:17:27.313 回答
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('_'))
于 2013-03-08T13:57:26.423 回答
3

记忆

记忆是一种优化技术,包括存储昂贵的函数调用的结果,并在使用相同参数再次调用函数时返回缓存的结果。

有一个无限版本,它将缓存它所看到的任何一对(输入参数,返回值);和一个有限的版本,它将使用 LRU 缓存缓存最后看到的 N 个输入参数及其结果。

方法的记忆:

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)

Wikipedia 页面包含有关在计算机科学中使用 Memoization 的大量信息。我将只指出一个简单的实际用途。

将常量的初始化推迟到最后可能的时刻

有时您有一个无法在类定义或创建时初始化的常量值。例如,常量表达式可能会使用来自不同类的另一个常量或方法,在您的类初始化之后,它们将被其他东西(Spring 等)插入。

在这种情况下,您可以将常量转换为 getter 并用@Memoized. 它只会计算一次,第一次被访问,然后缓存和重用值:

import groovy.transform.Memoized

@Memoized
def getMY_CONSTANT() {
    // compute the constant value using any external services needed
}
于 2015-05-06T10:32:09.210 回答
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)
}
}

输出:

富吧 10

于 2012-11-20T09:34:54.520 回答
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()'
于 2014-03-23T09:53:22.023 回答
2

如何在 groovy 中用几行代码构建 JSON 树?

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

于 2014-03-23T19:10:51.200 回答
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
于 2015-04-13T12:28:48.080 回答
1

多变量减速

1)单行多变量声明

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

2)使用不同的类型声明。

def (String a, int b) = ['Groovy', 1]
于 2016-02-11T07:34:46.090 回答
0

猫王算子

“猫王算子”是三元算子的缩写。这很方便的一个例子是,如果表达式解析为 false(如在 Groovy 中),则返回一个“合理的默认”值。一个简单的示例可能如下所示:

使用三元运算符,您必须重复要分配的值

displayCity = user.city ? user.city: 'UnKnown City'

使用 Elvis 运算符,如果不为假,则使用经过测试的值

displayCity = user.city ?: 'UnKnown City'
于 2015-12-15T15:04:58.763 回答
0

强制运算符

强制运算符 (as) 是强制转换的一种变体。强制将对象从一种类型转换为另一种类型,但它们不兼容赋值。举个例子:

Integer x = 123
String s = (String) x
Integer 不可分配给 String,因此它将在运行时产生 ClassCastException 这可以通过使用强制来修复:

Integer x = 123 String s = x as String
Integer 不可分配给 String,但使用 as 会将其强制为 String

于 2015-12-15T15:23:05.733 回答