Groovy入门指南(适用于Java开发者)

一、基本概念

Groovy是一种基于Java虚拟机(JVM)的动态开发语言,旨在扩展Java语法,提供更简洁、灵活和易于使用的编码方式。
本文是在会一定java语言开发的基础上进行的对比入门学习记录。

1. 特点:

  • 动态性:Groovy支持动态类型和动态元编程,使得代码更加灵活和易于编写。
  • 简洁性:Groovy的语法相对于Java来说更加简洁,可以用更少的代码实现相同的功能。
  • 闭包:Groovy提供了强大的闭包支持,使得函数式编程更为便捷。
  • def calculator = { operation, a, b ->
        switch (operation) {
            case "+": return a + b
            case "-": return a - b
            case "*": return a * b
            case "/": return a / b
            default: throw new IllegalArgumentException("Unknown operation")
        }
    }
    println calculator("+", 5, 3)
    
  • 无缝集成:Groovy可以直接使用Java类和库,与Java代码无缝集成,并且可以通过Java调用Groovy代码。
  • 领域专用语言(DSL):Groovy具有强大的DSL开发能力,允许开发人员根据特定领域的需求创建简洁、可读性高的领域特定语言。
  • 2. 编码风格

    与java基本相同,文件后缀为.groovy,示例:

    class Datapatch extends BaseModel {
    
        String patchId
        Date createdAt
        Date updatedAt
        DatapatchDetails details
    
        @Override
        String getPK() {
            return patchId
        }
    
        @Override
        void setPK(String pk) {
            patchId = pk
        }
    }
    

    3. 环境配置

    (1) jdk
    (2) groovy sdk

    二、语法区别

    1. 变量声明

    groovy除了可以使用所有java语言的变量声明方式,还可以用def来声明,def声明变量不用指定类型,访问权限默认为public

    def s1 = "test"
    def i1 = 123
    def f1 = 12.1f
    def c1 = 'c'
    
    int a = 10
    

    2. 方法

    使用 返回类型def关键字 定义,方法可以接收任意数量的参数,这些参数可以不申明类型, 如果不提供可见性修饰符,则该方法为 publicdef也可以用来声明函数。

    //def定义方法 不声明返回类型和参数类型
    def func1(name) {
        //$占位符插值
        println "$name"
    }
    
    //不声明返回类型,声明参数类型
    def func2(int i, int b) {
       return i + b
    }
    
    //返回类型声明方法,与java相同的方式
    int func3(int a){
        return a + 1
    }
    
    //可以在方法上指定默认值
    def func4(word = "Hello ", name) {
      println word + name
    }
    //调用的时候就可以不用传这个参数,如果传了就覆盖
    func4("groovy")//输出 Hello groovy
    
    //省略分号,类型,return
    def func5(a,b,c) {
       a,b,c //最后一行表达式的值会return
    }
    //省略 方法括号
    def number = func5 a,b,c
    
    //命名参数方法,用Map作为唯一参数
    def foo(Map args){
        println args.name
    }
    foo(name:'test',code:1)
    
    与java对比可省略的地方

    ○ 语句后面的分号
    ○ 调用方法的括号
    ○ 参数类型
    ○ return,方法最后一行的表达式结果默认return
    ○ 类、属性和方法的访问修饰符,省略默认为pblic,java省略默认是protected

    3. 类

    与java类似,只不过省略public修饰符
    ● 默认类的修饰符为public,所有class及方法都是public
    ● 没有可见性修饰符的字段默认为类的属性,会自动生成对应的settergetter方法。
    ● 类不需要与它的源文件有相同的名称,但还是建议采用相同的名称。
    ● 所有类 都继承自 GroovyObject

    class Test{
        //会自动对没有访问修饰符(默认是public)生产getter、setter
        String name
        int num
    
        //创建对象方式四和五 需要声明构造方法
        TestClass(name, age) {
            this.name = name
            this.age = age
        }
    
        //静态内部类
        static class StaticInnerClass{
    
        }
    
        //非静态内部类
        class InnerClass {
    
        }
    }
    
    //创建对象方式一:与java相同
    Test test1 = new Test()
    test1.name = "测试1"
    //创建对象方式二:当一个类没有构造方法的时候,其默认有一个命名参数构造方法
    Test test2 = new Test(name:"测试2",num:10)
    //创建对象方式三:with是一种特殊的用于减少重复代码的方式
    Test test3 = new Test()
    test3.with{
        name = "测试3"
        age = 20
    }
    //创建对象方式四:需要先有构造方法
     def test4 = ["测试4",30] as Test
    //创建对象方式五:需要先有构造方法
    Test test5 = ["测试5",40]
    
    //创建静态内部类
    def staticInner = new StaticInnerClass()
    
    //创建非静态内部类
    Test test = new Test()
    def inner = new InnerClass(test)
    

    4. for循环

    除了支持java的foreach和for i 形式,groovy还支持for in loop形式,支持遍历范围、列表、Map、数组和字符串等多种类型

    println "for in loop range"
    for ( i in 0..3 ) {
        println i
    }
    println "for in loop array"
    for ( i in [0, 1, 2, 3] ) {
        println i
    }
    println "for in loop map"
    def map = ['a':1, 'b':2, 'c':3]
    for ( v in map.values() ) {
        println v
    }
    

    5. 数据类型

    GString(插值字符串groovy.lang.GStrin)

    groovy独有,GString是可变的,GString和String即使有相同的字面量,它们的hashCodes的值也可能不同,因此应该避免使用GString作为Map的key

    //通过占位符插值的字符串是可变的,所以这种字符串类型就是GString
    String s1 = "123"
    GString string = "$s1"
    println(string.hashCode())
    String s2 = "123"
    println(s2.hashCode())
    String s3 = "123"
    println(s3.hashCode())
    //输出 48727 48690 48690
    
    //单引号字符串,原样输出不可变,不支持插值和转义
    def s1 = 'Single-quoted strings'
    //双引号字符串,支持插值和转义
    def ss = "111"
    def s1 = "Double-quoted strings $ss"
    //三引号字符串,常用于多行文本,支持插值和转义
    def s1 = """Triple-quoted strings
                This is second line"""
    
    Groovy中的容器类

    List:默认实现类ArrayList,可以直接使用[]声明一个lsit

    //定义list
    //groovy方式
    def list1 = [1,2,3]
    def list2 = []
    //java方式
    def list3 = new ArrayList()
    
    //取值
    //java方式
    println list1.get(0)
    //groovy方式
    println list1[-1]
    println list1.getAt(-2)
    
    //添加值
    list1 << 4
    list1.leftShift(5)
    list1 += 6
    list1.plus 7
    //插入指定位置
    list1[1] = 21
    list1.add(1,'e')
    //插入另一个列表所有值
    list1.addAll(1,[11,22])
    //这种方式第一个值必须是list
    list += [1,2] + 12 + 13 +  [11,22]
    
    //删除值
    //删除所有值等于1的值
    list1 -= 1
    list1.remove 1
    
    //遍历
    list1.foreach{
      //it是对应当前元素的隐式参数
      println it
    }
    //带索引遍历
    list1.eachWithIndex { it,idx->
      println "value:$it,index:$idx"
    }
    
    //过滤使用的一些方法
    find/findAll/findIndexOf/indexOf/every/any/groupBy
    //常用方法collect,用来转换列表元素
    def numbers = [1, 2, 3, 4, 5]
    def squaredNumbers = numbers.collect { it * it }
    println "Squared numbers: $squaredNumbers"
    // 输出 Squared numbers: [1, 4, 9, 16, 25]
    

    Map:直接使用[:]声明,默认的实现类为java.util.LinkedHashMap

    def key = 'name' 
    //key 就是 "key"
    def person = [key: '耗子尾汁'] 
    //用括号表示传递一个变量,key='name'
    person = [(key): '耗子尾汁']
    
    //生成空map
    def m = new HashMap()
    def map = [:]
    

    三、闭包

    1. 闭包是一个开放的、匿名的、可以接受参数和返回值并分配给变量。
    2. 也可以看做是一个对象,Closure类型的对象。
    3. 语法就是一段使用花括号{}括起来的代码块,闭包默认存在一个隐式形参it
      def 闭包变量名 = { [形参1, 形参n] -> 闭包体 }
    4. 示例
    //只有一个参数或没有参数,形参可以不写
    def func1 = {print it}
    func1.call("test")
    
    	//多个参数则要显示定义
    	def func2 = {name,age ->{ println "name:$name,age:$age"}}
    	func2.call("test1",10)
    
    1. 三个关键变量
      this:代表定义闭包的类。只有一个闭包,并且没有嵌套闭包的情况下,this、owner、delegate指向相同。this不能修改。
      owner:代表闭包定义处的类或者对象。在闭包嵌套闭包中,owner、delegate指向离它们最近的闭包对象。this指向最近的类。owner不能修改。
      delegate:可以代表任意对象,默认情况下owner和delegate是一样的。delegate释义为委托,所以可以手动修改指向其他对象。 delegate可以修改。
    2. 使用场景
      在函数式编程中,将闭包作为参数传递给其他方法

    四、trait

    Trait具有Java Interface和抽象类的双重特征,介于java的接口和抽象类之间的,主要用来解决多重继承需求,在使用上:
    ● 可以定义属性,非final类型的,可以修改
    ● 像java-interface-default方法一样,有具体实现的方法
    ● 没有具体实现的方法必须用abstract修饰为抽象方法
    ● 可以使用extendsimplement来继承trait类
    ● 当不同Trait定义了相同的field或者method时, 将它们同时implements的时候,后面的会覆盖前面的。比如 class A implements B,C ,那么C的field和method会覆盖B的

    trait TestInterface {
        String name
    
        abstract void method1()
    
        default String method2(){
            name = "cccc"
            return name
        }
    }
    
    //实现类
    class TestInterfaceImpl implements TestInterface {
    
        @Override
        void method1() {
            System.out.println(this.method2());
        }
    
        static void main(String[] args) {
            TestInterface t = new TestInterfaceImpl()
            t.method1()
        }
    }
    

    五、元编程

    1、概念

    元编程是指在运行时创建、修改或操纵程序的能力。在代码执行过程中对程序进行自省和改变,从而实现更高级别的抽象和灵活性。

    1.1 优势和用途

    动态性:元编程使得在运行时创建、修改或删除类、对象、方法等成为可能。这种动态性使得我们可以根据运行时的情况来改变程序的行为,从而增强了程序的灵活性。
    简化重复工作:通过使用元编程,我们可以编写更少的代码来实现相同的功能。通过动态地生成代码、修改代码或使用模板,我们可以避免冗长、重复的代码并提高开发效率。
    领域特定语言(DSL):元编程使得创建领域特定语言成为可能。通过定义自己的语法和语义,我们可以为特定领域提供更易于理解和使用的接口。
    框架扩展:许多开源框架都使用元编程来实现其内部机制。通过利用元编程能力,我们可以扩展这些框架的功能,满足我们特定的需求。

    1.2 流程

    简单来说就是运行时期的一个策略。例如调用一个对象的某一个方法,如果这个方法不存在,编译器也不会报错,而是通过一系列的查找最终确定是调用还是抛出异常,下面就是简化后的流程:

    1.3 使用示例

    现在有一个类结构如下:

    package com.alvin.learn.test
    
    class TestClass {
        String name
        Integer age
    }
    
    1.3.1 动态添加属性
    //TestClass本来没有code属性,动态添加一个
    TestClass.metaClass.code = 10
    def test = new TestClass()
    println test.code
    //可以正常修改
    test.code = 2
    println test.code
    
    //输出 10 2
    
    1.3.2 动态添加方法
    def test = new TestClass()
    //1、现在类里面没有testMethod方法,所以执行会报 找不到此方法
    test.testMethod("this is args")
    
    //2、在TestClass 里面重写 invokeMethod方法
    def invokeMethod(String name,Object args){
        println("调用了invokeMethod,name:$name,args:$args")
    }
    
    //3、再次执行1处的调用,会打印
    //调用了invokeMethod,name:testMethod,args:[this is args]
    
    //4、在TestClass 里面重写 methodMissing方法
    def methodMissing(String name,Object args){
        println("调用了imethodMissing,name:$name,args:$args")
    }
    //5、再次执行1处的调用,会打印
    //调用了imethodMissing,name:testMethod,args:t[this is args]
    
    //6、动态创建方法
    TestClass.metaClass.testMethod = {
        println it
    }
    def test = new TestClass()
    test.testMethod("this is args")
    //打印 this is args
    
    //7、动态添加静态方法
    TestClass.metaClass.static.testStaticMethod = {
        println 'this is a new static method'
    }
    TestClass.testStaticMethod()
    //打印 this is a new static method
    
    1.3.3 全局使用

    以上使用都是临时的,在别的类中使用添加的方法需要重新注入,以下使全局使用示例。

    class TestManager {
        static TestClass createTest(String name, int age) {
            //调用不存在的方法
            return TestClass.createTest(name, age)
        }
    }
    
    
    class ApplicationManager {
        static void init() {
            //设置全局
            ExpandoMetaClass.enableGlobally()
            //创建 Person 静态方法
            TestClass.metaClass.static.createTest = {
                String name, Integer age ->
                    return new TestClass(name: name, age: age)
            }
        }
    }
    
    
    class Main {
        public static void main(String[] args) {
            println '应用程序正在启动'
            //初始化
            ApplicationManager.init()
            println '应用程序初始化完成'
    
            def test = TestManager.createTest("张三", 20)
            println test.name + "----" + test.age
        }
    }
    
    1.3.4 动态创建类
    // 创建一个新的类
    def myClass = this.class.classLoader.parseClass("""
    class MyDynamicClass {
        String name
     
        MyDynamicClass(String name) {
            this.name = name
        }
    }
    """)
     
    // 使用MetaClass为类添加方法
    MyDynamicClass.metaClass.sayHello = { ->
        "Hello, my name is ${delegate.name}"
    }
     
    // 实例化对象并调用新方法
    def obj = new MyDynamicClass('Groovy')
    println obj.sayHello()
    

    作者:black_dawn

    物联沃分享整理
    物联沃-IOTWORD物联网 » Groovy入门指南(适用于Java开发者)

    发表评论