Groovy的隐藏功能?


78

似乎Groovy在该线程中被遗忘了,所以我只想向Groovy询问相同的问题。

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

也可以看看:

  1. Python的隐藏功能
  2. Ruby的隐藏功能
  3. Perl的隐藏功能
  4. Java的隐藏功能

Answers:


56

使用扩展点运算符

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

这是的快捷方式animals.collect { it.size() }


第三行是什么意思?
ripper234'2010-10-21

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因为不需要调用setter方法,所以(etc.)和后者之间是否会进行更典型的比较?
菲利普

37

使用哈希作为伪对象。

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

结合鸭子打字,您可以使用这种方法大有帮助。甚至不需要淘汰“ as”运算符。


2
Groovy的新功能-确实很好。
史蒂夫·B。

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

不完全是,它是缩短的三元运算符。我在上面写了一篇文章:collinharrington.net/blog/2008/10/groovy-elvis-operator您也可以在其中进行完整的表达:-)
Colin Harrington 2010年

答案中发布的代码无法编译,因为关键字“默认”被用作变量。使用“ d”代替代码。
2011年

2
根本没有重要的原因,只需遵守OP建议的约定即可。当时我没有考虑我的动作会带来的令人耳目一新的效果。
gotomanners 2011年

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 2012年

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'2010-10-22

3
Object Foo没有定义名为B的静态方法。但是,您可以通过添加一个名为“ $ static_methodMissing(String,Object)”的方法并在那里实现所需的任何内容来即时实现。每当调用静态方法并且对象没有定义该静态方法时,就会调用此魔术方法。
Jen S.


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

标准功能,但仍然非常不错。

ObjectGraphBuilder

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


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

嗯,多行字符串的美。每种语言都应该采用这些。
ripper234'2010-10-22

2
不知道为什么可以扩展多行字符串以允许多行和单行字符串时,为什么多行字符串需要使用“”“作为分隔符。
2011年

2
@VorgvanGeir使用“”表示您不必转义“。
未定义的

1
@Brian True,但是“”“ a \ bc” de“ f \ g”“”无法编译,因为您必须对\或\ g进行转义,并且\ b就像退格键一样,除非您对其进行转义。什么时候不需要转义“当您仍然需要转义字符串中的每个其他特殊顺序时有什么意义?”
Vorg van Geir 2012年

因为我们中有些人希望能够编写“ foo \ tbar”。但是Groovy也有:println(/ a \ bc“ de” f \ g /)//-> a \ bc“ de” f \ g
DarkStar 2014年

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

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。杀手级功能!

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 []在第一行有必要吗?
卡米尔2015年

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

7

@代表

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

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

def bar = new Bar()

assert "footest" == bar.footest()

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 msg”和“ int val”中的“ String”和“ int”省略掉—我为清楚起见保留了它们。


我希望情况确实如此,但是当前Groovy(1.6)仅支持对象构造函数的命名参数。您可以使用此语法进行方法调用,但是它将所有命名参数打包到Map中,然后查找foo(Map)。
科迪·卡斯特琳

对于您认为我所说的暗示不同,我感到困惑。
罗伯特·菲舍尔

4

我认为这是闭包作为参数和parameter-default-values的组合:

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

不,我的意思是将代码转换为数据,使之成为一个数据数组,相当于一个参数列表,该参数列表通常是静态的。但是我明白你的意思,这取决于你的观点。我正在从将现有静态代码重构为更动态的代码的角度来看待它。
LuisMuñiz'13

也许是“使用数据驱动设计时”?
DarkStar'7

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)

维基百科页面上有关于计算机科学中记忆化用法的广泛信息。我只是指出一种简单的实际用途。

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

有时,您有一个常量值,无法在类定义或创建时初始化。例如,常量表达式可以使用另一个常量或来自不同类的方法,该类将在初始化类后由其他东西(例如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

如何在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"}}}


2

安全导航员

安全导航运算符用于避免NullPointerException。通常,当您具有对对象的引用时,可能需要在访问对象的方法或属性之前验证其是否为null。为了避免这种情况,安全的导航运算符将只返回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


0

强制运算符

强制运算符(as)是强制转换的变体。Coercion将对象从一种类型转换为另一种类型,而这些对象不兼容分配。让我们举个例子:

整数x = 123
字符串s =(字符串)x
整数不能分配给字符串,因此它将在运行时产生ClassCastException。 可以通过使用强制方法来解决此问题:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.