Groovy记录

官方文档

相关记录

  • keywords
    asdeftraitin

  • String

    • Single quoted string:
      'a single quoted string'
    • Triple single quoted string:
      '''a triple single quoted string'''
    • Double quoted string:
      "a double quoted string"
      def name = 'Guillaume' // a plain string
      def greeting = "Hello ${name}"
      
    • Triple double quoted string
      def name = 'Groovy'
      def template = """
          Dear Mr ${name},
          You're the winner of the lottery!
          Yours sincerly,
          Dave
      """
      assert template.toString().contains('Groovy')
      
  • Numbers Underscore in literals

    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
    
  • Lists

    def letters = ['a', 'b', 'c', 'd']
    assert letters[0] == 'a'     
    assert letters[1] == 'b'
    
    assert letters[-1] == 'd'    //神奇吧!从后往前取,用负数!
    assert letters[-2] == 'c'
    
  • Arrays

    String[] arrStr = ['Ananas', 'Banana', 'Kiwi']  
    def numArr = [1, 2, 3] as int[]   
    
  • Maps

    def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']  
    assert colors['red'] == '#FF0000'    
    assert colors.green  == '#00FF00'    
    
    colors['pink'] = '#FF00FF'           
    colors.yellow  = '#FFFF00'           
    
    assert colors.pink == '#FF00FF'
    assert colors['yellow'] == '#FFFF00'
    
    assert colors instanceof java.util.LinkedHashMap 
    
  • Method pointer operator

    def str = 'example of method reference'            
    def fun = str.&toUpperCase                         
    def upper = fun()                                  
    assert upper == str.toUpperCase() 
    

    它可以作为Closure参数:

    def transform(List elements, Closure action) {                    
        def result = []
        elements.each {
            result << action(it)
        }
        result
    }
    String describe(Person p) {                                       
        "$p.name is $p.age"
    }
    def action = this.&describe                                       
    def list = [
        new Person(name: 'Bob',   age: 42),
        new Person(name: 'Julia', age: 35)]                           
    assert transform(list, action) == ['Bob is 42', 'Julia is 35'] 
    
  • Spread operator

    class Car {
        String make
        String model
    }
    def cars = [
           new Car(make: 'Peugeot', model: '508'),
           new Car(make: 'Renault', model: 'Clio')]       
    def makes = cars*.make                                
    assert makes == ['Peugeot', 'Renault']  
    
  • Range operator

    def range = 0..5                                    
    assert (0..5).collect() == [0, 1, 2, 3, 4, 5]       
    assert (0..<5).collect() == [0, 1, 2, 3, 4]         
    assert (0..5) instanceof List                       
    assert (0..5).size() == 6    
    
  • Spaceship operator

    assert (1 <=> 1) == 0
    assert (1 <=> 2) == -1
    assert (2 <=> 1) == 1
    assert ('a' <=> 'z') == -1
    
  • Membership operator

    def list = ['Grace','Rob','Emmy']
    assert ('Emmy' in list)     
    
  • Identity operator
    In Groovy, using == to test equality is different from using the same operator in Java. In Groovy, it is calling equals. If you want to compare reference equality, you should use is like in the following example:

    def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        
    def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        
    assert list1 == list2                                       
    assert !list1.is(list2) 
    
  • Coercion operator

    Integer x = 123
    String s = x as String 
    
  • Call operator

    class MyCallable {
        int call(int x) {           
            2*x
        }
    }
    
    def mc = new MyCallable()
    assert mc.call(2) == 4          
    assert mc(2) == 4
    
  • public static void main vs script
    Groovy supports both scripts and classes. Take the following code for example:
    Main.groovy

    class Main {                                    
        static void main(String... args) {          
            println 'Groovy world!'                 
        }
    }
    

    This is typical code that you would find coming from Java, where code has to be embedded into a class to be executable. Groovy makes it easier, the following code is equivalent:

    println 'Groovy world!'
    
  • Script class
    A script is always compiled into a class. The Groovy compiler will compile the class for you, with the body of the script copied into a run method. The previous example is therefore compiled as if it was the following:
    Main.groovy

      import org.codehaus.groovy.runtime.InvokerHelper
      class Main extends Script {                     
          def run() {                                 
              println 'Groovy world!'                 
          }
          static void main(String[] args) {           
              InvokerHelper.runScript(Main, args)     
          }
      }
    

    You can also mix methods and code. The generated script class will carry all methods into the script class, and assemble all script bodies into the run method:

    println 'Hello'                                 
    int power(int n) { 2**n }                       
    println "2^6==${power(6)}"   
    

    This code is internally converted into:

    import org.codehaus.groovy.runtime.InvokerHelper
    class Main extends Script {
        int power(int n) { 2** n}                   
        def run() {
            println 'Hello'                         
            println "2^6==${power(6)}"              
        }
        static void main(String[] args) {
            InvokerHelper.runScript(Main, args)
        }
    }
    
  • Class
    Groovy classes are very similar to Java classes, and are compatible with Java ones at JVM level. They may have methods, fields and properties (think JavaBean properties but with less boilerplate). Classes and class members can have the same modifiers (public, protected, private, static, etc) as in Java with some minor differences at the source level which are explained shortly.

    The key differences between Groovy classes and their Java counterparts are:

    • Classes or methods with no visibility modifier are automatically public (a special annotation can be used to achieve package private visibility).

    • Fields with no visibility modifier are turned into properties automatically, which results in less verbose code, since explicit getter and setter methods aren’t needed. More on this aspect will be covered in the fields and properties section.

    • Classes do not need to have the same base name as their source file definitions but it is highly recommended in most scenarios (see also the next point about scripts).

    • One source file may contain one or more classes (but if a file contains any code not in a class, it is considered a script). Scripts are just classes with some special conventions and will have the same name as their source file (so don’t include a class definition within a script having the same name as the script source file).

  • Constructors

    class PersonConstructor {
        String name
        Integer age
    
        PersonConstructor(name, age) {          
            this.name = name
            this.age = age
        }
    }
    
    def person1 = new PersonConstructor('Marie', 1)  
    def person2 = ['Marie', 2] as PersonConstructor  
    PersonConstructor person3 = ['Marie', 3]  
    
    class PersonWOConstructor {                                  
        String name
        Integer age
    }
    
    def person4 = new PersonWOConstructor()                      
    def person5 = new PersonWOConstructor(name: 'Marie')         
    def person6 = new PersonWOConstructor(age: 1)                
    def person7 = new PersonWOConstructor(name: 'Marie', age: 2)
    
  • Method
    1.Named arguments
    Like constructors, normal methods can also be called with named arguments. They need to receive the parameters as a map. In the method body, the values can be accessed as in normal maps (map.key).

    def foo(Map args) { "${args.name}: ${args.age}" }
    foo(name: 'Marie', age: 1)
    

    2.Default arguments

    def foo(String par1, Integer par2 = 1) { [name: par1, age: par2] }
    assert foo('Marie').age == 1
    
  • Exception declaration
    Groovy automatically allows you to treat checked exceptions like unchecked exceptions. This means that you don’t need to declare any checked exceptions that a method may throw as shown in the following example which can throw a FileNotFoundException if the file isn’t found:

    def badRead() {
        new File('doesNotExist.txt').text
    }
    
    shouldFail(FileNotFoundException) {
        badRead()
    }
    
  • Fields and properties

    A field is a member of a class or a trait which has:

    • a mandatory access modifier (public, protected, or private)
    • one or more optional modifiers (static, final, synchronized)
    • an optional type
    • a mandatory name

    A property is an externally visible feature of a class. Rather than just using a public field to represent such features (which provides a more limited abstraction and would restrict refactoring possibilities), the typical convention in Java is to follow JavaBean conventions, i.e. represent the property using a combination of a private backing field and getters/setters. Groovy follows these same conventions but provides a simpler approach to defining the property. You can define a property with:

    • an absent access modifier (no public, protected or private)
    • one or more optional modifiers (static, final, synchronized)
    • an optional type
    • a mandatory name
  • Annotation member values
    However it is possible to omit value= in the declaration of the value of an annotation if the member value is the only one being set:

    @interface Page {
        String value()
        int statusCode() default 200
    }
    
    @Page(value='/home')                    
    void home() {
        // ...
    }
    
    @Page('/users')                         
    void userList() {
        // ...
    }
    
    @Page(value='error',statusCode=404)     
    void notFound() {
        // ...
    }
    
  • Closure annotation parameters
    An interesting feature of annotations in Groovy is that you can use a closure as an annotation value. Therefore annotations may be used with a wide variety of expressions and still have IDE support. For example, imagine a framework where you want to execute some methods based on environmental constraints like the JDK version or the OS. One could write the following code:

    class Tasks {
        Set result = []
        void alwaysExecuted() {
            result << 1
        }
        @OnlyIf({ jdk>=6 })
        void supportedOnlyInJDK6() {
            result << 'JDK 6'
        }
        @OnlyIf({ jdk>=7 && windows })
        void requiresJDK7AndWindows() {
            result << 'JDK 7 Windows'
        }
    }
    

    For the @OnlyIf annotation to accept a Closure as an argument, you only have to declare the value as a Class:

    @Retention(RetentionPolicy.RUNTIME)
    @interface OnlyIf {
        Class value()                    
    }
    
    class Runner {
        static <T> T run(Class<T> taskClass) {
            def tasks = taskClass.newInstance()                                         
            def params = [jdk:6, windows: false]                                        
            tasks.class.declaredMethods.each { m ->                                     
                if (Modifier.isPublic(m.modifiers) && m.parameterTypes.length == 0) {   
                    def onlyIf = m.getAnnotation(OnlyIf)                                
                    if (onlyIf) {
                        Closure cl = onlyIf.value().newInstance(tasks,tasks)            
                        cl.delegate = params                                            
                        if (cl()) {                                                     
                            m.invoke(tasks)                                             
                        }
                    } else {
                        m.invoke(tasks)                                                 
                    }
                }
            }
            tasks                                                                       
        }
    }
    
  • Traits
    Traits are a structural construct of the language which allows:

    • composition of behaviors
    • runtime implementation of interfaces
    • behavior overriding
    • compatibility with static type checking/compilation
    trait FlyingAbility {                           
            String fly() { "I'm flying!" }          
    }
    
    class Bird implements FlyingAbility {}          
    def b = new Bird()                              
    assert b.fly() == "I'm flying!"   
    

Closures


Closure太重要了,以至于,我要把它单独拎出来。
先看看官方对它的定义:

A closure in Groovy is an open, anonymous, block of code that can take arguments, return a value and be assigned to a variable. A closure may reference variables declared in its surrounding scope. In opposition to the formal definition of a closure, Closure in the Groovy language can also contain free variables which are defined outside of its surrounding scope. While breaking the formal concept of a closure, it offers a variety of advantages which are described in this chapter.

  • Defining a closure

    { [closureParameters -> ] statements }
    

    Where [closureParameters->] is an optional comma-delimited list of parameters, and statements are 0 or more Groovy statements. The parameters look similar to a method parameter list, and these parameters may be typed or untyped.

    When a parameter list is specified, the -> character is required and serves to separate the arguments from the closure body. The statements portion consists of 0, 1, or many Groovy statements.

    { item++ }                                          
    
    { -> item++ }                                       
    
    { println it }                                      
    
    { it -> println it }                                
    
    { name -> println name }                            
    
    { String x, int y ->                                
        println "hey ${x} the value is ${y}"
    }
    
    { reader ->                                         
        def line = reader.readLine()
        line.trim()
    }
    
  • Closures as an object
    A closure is an instance of the groovy.lang.Closure class, making it assignable to a variable or a field as any other variable, despite being a block of code:

    def listener = { e -> println "Clicked on $e.source" }      
    assert listener instanceof Closure
    Closure callback = { println 'Done!' }                      
    Closure<Boolean> isTextFile = {
        File it -> it.name.endsWith('.txt')                     
    }
    
  • Calling a closure

    def code = { 123 }
    assert code() == 123
    assert code.call() == 123
    

    Unlike a method, a closure always returns a value when called.

  • Normal parameters
    Parameters of closures follow the same principle as parameters of regular methods:

    • an optional type
    • a name
    • an optional default value

    Parameters are separated with commas:

    def closureWithOneArg = { str -> str.toUpperCase() }
    assert closureWithOneArg('groovy') == 'GROOVY'
    
    def closureWithOneArgAndExplicitType = { String str -> str.toUpperCase() }
    assert closureWithOneArgAndExplicitType('groovy') == 'GROOVY'
    
    def closureWithTwoArgs = { a,b -> a+b }
    assert closureWithTwoArgs(1,2) == 3
    
    def closureWithTwoArgsAndExplicitTypes = { int a, int b -> a+b }
    assert closureWithTwoArgsAndExplicitTypes(1,2) == 3
    
    def closureWithTwoArgsAndOptionalTypes = { a, int b -> a+b }
    assert closureWithTwoArgsAndOptionalTypes(1,2) == 3
    
    def closureWithTwoArgAndDefaultValue = { int a, int b=2 -> a+b }
    assert closureWithTwoArgAndDefaultValue(1) == 3
    
  • Implicit parameter
    When a closure does not explicitly define a parameter list (using ->), a closure always defines an implicit parameter, named it.

    If you want to declare a closure which accepts no argument and must be restricted to calls without arguments, then you must declare it with an explicit empty argument list:

    def magicNumber = { -> 42 }
    
    // this call will fail because the closure doesn't accept any argument
    magicNumber(11)
    
  • Delegation strategy
    A closure actually defines 3 distinct things:

    • this corresponds to the enclosing class where the closure is defined
    • owner corresponds to the enclosing object where the closure is defined, which may be either a class or a closure
    • delegate corresponds to a third party object where methods calls or properties are resolved whenever the receiver of the message is not defined
  • this
    Closure里的this就是Closure没有this!它只是指向定义了这个Closure的外部的第一层的Class对象

  • owner
    就是拥有(定义)这个Closure的对象,可能是Class也可能是Closure

  • delegage

    class Person {
        String name
    }
    def p = new Person(name:'Igor')
    def cl = { name.toUpperCase() }                 
    cl.delegate = p                                 
    assert cl() == 'IGOR'
    

    但是,

    class Person {
        String name
        def pretty = { "My name is $name" }             
        String toString() {
            pretty()
        }
    }
    class Thing {
        String name                                     
    }
    
    def p = new Person(name: 'Sarah')
    def t = new Thing(name: 'Teapot')
    
    // Using the default strategy, the name property is resolved on the owner first
    assert p.toString() == 'My name is Sarah'     
    // so if we change the delegate to t which is an instance of Thing      
    p.pretty.delegate = t     
    // there is no change in the result: name is first resolved on the owner of   the closure                          
    assert p.toString() == 'My name is Sarah' 
    

    最后一行,为什么不变?

    因为,Closure.OWNER_FIRST is the default strategy
    首先从owner中去找,找到了name属性,ok,那就不管后面又给delegate赋了什么鬼值了

    再来一个栗子:

    class Person {
        String name
        int age
        def fetchAge = { age }
    }
    class Thing {
        String name
    }
    
    def p = new Person(name:'Jessica', age:42)
    def t = new Thing(name:'Printer')
    def cl = p.fetchAge
    cl.delegate = p
    assert cl() == 42
    cl.delegate = t
    assert cl() == 42
    cl.resolveStrategy = Closure.DELEGATE_ONLY
    cl.delegate = p
    assert cl() == 42
    cl.delegate = t
    try {
        cl()
        assert false
    } catch (MissingPropertyException ex) {
        // "age" is not defined on the delegate
    }
    
  • 在GString中使用Closure

    class Person {
        String name
        String toString() { name }
    }
    def sam = new Person(name:'Sam')
    def lucy = new Person(name:'Lucy')
    def p = sam
    // Create a GString with lazy evaluation of "p"
    def gs = "Name: ${-> p}"
    assert gs == 'Name: Sam'
    p = lucy
    assert gs == 'Name: Lucy'
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。