找回密码
 会员注册
查看: 32|回复: 0

Groovy入门(有java基础)

[复制链接]

4

主题

0

回帖

13

积分

新手上路

积分
13
发表于 2024-9-3 15:28:21 | 显示全部楼层 |阅读模式
一、基本概念Groovy是一种基于Java虚拟机(JVM)的动态开发语言,旨在扩展Java语法,提供更简洁、灵活和易于使用的编码方式。本文是在会一定java语言开发的基础上进行的对比入门学习记录。1.特点:动态性:Groovy支持动态类型和动态元编程,使得代码更加灵活和易于编写。简洁性:Groovy的语法相对于Java来说更加简洁,可以用更少的代码实现相同的功能。闭包:Groovy提供了强大的闭包支持,使得函数式编程更为便捷。defcalculator={operation,a,b->switch(operation){case"+":returna+bcase"-":returna-bcase"*":returna*bcase"/":returna/bdefault:thrownewIllegalArgumentException("Unknownoperation")}}printlncalculator("+",5,3)12345678910无缝集成:Groovy可以直接使用Java类和库,与Java代码无缝集成,并且可以通过Java调用Groovy代码。领域专用语言(DSL):Groovy具有强大的DSL开发能力,允许开发人员根据特定领域的需求创建简洁、可读性高的领域特定语言。2.编码风格与java基本相同,文件后缀为.groovy,示例:classDatapatchextendsBaseModel{StringpatchIdDatecreatedAtDateupdatedAtDatapatchDetailsdetails@OverrideStringgetPK(){returnpatchId}@OverridevoidsetPK(Stringpk){patchId=pk}}12345678910111213141516173.环境配置(1)jdk(2)groovysdk二、语法区别1.变量声明groovy除了可以使用所有java语言的变量声明方式,还可以用def来声明,def声明变量不用指定类型,访问权限默认为publicdefs1="test"defi1=123deff1=12.1fdefc1='c'inta=101234562.方法使用返回类型或def关键字定义,方法可以接收任意数量的参数,这些参数可以不申明类型,如果不提供可见性修饰符,则该方法为public。def也可以用来声明函数。//def定义方法不声明返回类型和参数类型deffunc1(name){//$占位符插值println"$name"}//不声明返回类型,声明参数类型deffunc2(inti,intb){returni+b}//返回类型声明方法,与java相同的方式intfunc3(inta){returna+1}//可以在方法上指定默认值deffunc4(word="Hello",name){printlnword+name}//调用的时候就可以不用传这个参数,如果传了就覆盖func4("groovy")//输出Hellogroovy//省略分号,类型,returndeffunc5(a,b,c){a,b,c//最后一行表达式的值会return}//省略方法括号defnumber=func5a,b,c//命名参数方法,用Map作为唯一参数deffoo(Mapargs){printlnargs.name}foo(name:'test',code:1)1234567891011121314151617181920212223242526272829303132333435与java对比可省略的地方○语句后面的分号○调用方法的括号○参数类型○return,方法最后一行的表达式结果默认return○类、属性和方法的访问修饰符,省略默认为pblic,java省略默认是protected3.类与java类似,只不过省略public修饰符●默认类的修饰符为public,所有class及方法都是public●没有可见性修饰符的字段默认为类的属性,会自动生成对应的setter和getter方法。●类不需要与它的源文件有相同的名称,但还是建议采用相同的名称。●所有类都继承自GroovyObjectclassTest{//会自动对没有访问修饰符(默认是public)生产getter、setterStringnameintnum//创建对象方式四和五需要声明构造方法TestClass(name,age){this.name=namethis.age=age}//静态内部类staticclassStaticInnerClass{}//非静态内部类classInnerClass{}}//创建对象方式一:与java相同Testtest1=newTest()test1.name="测试1"//创建对象方式二:当一个类没有构造方法的时候,其默认有一个命名参数构造方法Testtest2=newTest(name:"测试2",num:10)//创建对象方式三:with是一种特殊的用于减少重复代码的方式Testtest3=newTest()test3.with{name="测试3"age=20}//创建对象方式四:需要先有构造方法deftest4=["测试4",30]asTest//创建对象方式五:需要先有构造方法Testtest5=["测试5",40]//创建静态内部类defstaticInner=newStaticInnerClass()//创建非静态内部类Testtest=newTest()definner=newInnerClass(test)12345678910111213141516171819202122232425262728293031323334353637383940414243444.for循环除了支持java的foreach和fori形式,groovy还支持forinloop形式,支持遍历范围、列表、Map、数组和字符串等多种类型println"forinlooprange"for(iin0..3){printlni}println"forinlooparray"for(iin[0,1,2,3]){printlni}println"forinloopmap"defmap=['a':1,'b':2,'c':3]for(vinmap.values()){printlnv}123456789101112135.数据类型GString(插值字符串groovy.lang.GStrin)groovy独有,GString是可变的,GString和String即使有相同的字面量,它们的hashCodes的值也可能不同,因此应该避免使用GString作为Map的key//通过占位符插值的字符串是可变的,所以这种字符串类型就是GStringStrings1="123"GStringstring="$s1"println(string.hashCode())Strings2="123"println(s2.hashCode())Strings3="123"println(s3.hashCode())//输出487274869048690//单引号字符串,原样输出不可变,不支持插值和转义defs1='Single-quotedstrings'//双引号字符串,支持插值和转义defss="111"defs1="Double-quotedstrings$ss"//三引号字符串,常用于多行文本,支持插值和转义defs1="""Triple-quotedstringsThisissecondline"""123456789101112131415161718Groovy中的容器类List:默认实现类ArrayList,可以直接使用[]声明一个lsit//定义list//groovy方式deflist1=[1,2,3]deflist2=[]//java方式deflist3=newArrayList()//取值//java方式printlnlist1.get(0)//groovy方式printlnlist1[-1]printlnlist1.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"valueit,indexidx"}//过滤使用的一些方法find/findAll/findIndexOf/indexOf/every/any/groupBy//常用方法collect,用来转换列表元素defnumbers=[1,2,3,4,5]defsquaredNumbers=numbers.collect{it*it}println"SquarednumberssquaredNumbers"//输出Squarednumbers:[1,4,9,16,25]12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849Map:直接使用[:]声明,默认的实现类为java.util.LinkedHashMapdefkey='name'//key就是"key"defperson=[key:'耗子尾汁']//用括号表示传递一个变量,key='name'person=[(key):'耗子尾汁']//生成空mapdefm=newHashMap()defmap=[:]123456789三、闭包闭包是一个开放的、匿名的、可以接受参数和返回值并分配给变量。也可以看做是一个对象,Closure类型的对象。语法就是一段使用花括号{}括起来的代码块,闭包默认存在一个隐式形参itdef闭包变量名={[形参1,形参n]->闭包体}示例//只有一个参数或没有参数,形参可以不写deffunc1={printit}func1.call("test") //多个参数则要显示定义 deffunc2={name,age->{println"namename,ageage"}} func2.call("test1",10)1234567三个关键变量●this:代表定义闭包的类。只有一个闭包,并且没有嵌套闭包的情况下,this、owner、delegate指向相同。this不能修改。●owner:代表闭包定义处的类或者对象。在闭包嵌套闭包中,owner、delegate指向离它们最近的闭包对象。this指向最近的类。owner不能修改。●delegate:可以代表任意对象,默认情况下owner和delegate是一样的。delegate释义为委托,所以可以手动修改指向其他对象。delegate可以修改。使用场景在函数式编程中,将闭包作为参数传递给其他方法四、traitTrait具有JavaInterface和抽象类的双重特征,介于java的接口和抽象类之间的,主要用来解决多重继承需求,在使用上:●可以定义属性,非final类型的,可以修改●像java-interface-default方法一样,有具体实现的方法●没有具体实现的方法必须用abstract修饰为抽象方法●可以使用extends、implement来继承trait类●当不同的Trait定义了相同的field或者method时,将它们同时implements的时候,后面的会覆盖前面的。比如classAimplementsB,C,那么C的field和method会覆盖B的traitTestInterface{Stringnameabstractvoidmethod1()defaultStringmethod2(){name="cccc"returnname}}//实现类classTestInterfaceImplimplementsTestInterface{@Overridevoidmethod1(){System.out.println(this.method2());}staticvoidmain(String[]args){TestInterfacet=newTestInterfaceImpl()t.method1()}}123456789101112131415161718192021222324五、元编程1、概念元编程是指在运行时创建、修改或操纵程序的能力。在代码执行过程中对程序进行自省和改变,从而实现更高级别的抽象和灵活性。1.1优势和用途●动态性:元编程使得在运行时创建、修改或删除类、对象、方法等成为可能。这种动态性使得我们可以根据运行时的情况来改变程序的行为,从而增强了程序的灵活性。●简化重复工作:通过使用元编程,我们可以编写更少的代码来实现相同的功能。通过动态地生成代码、修改代码或使用模板,我们可以避免冗长、重复的代码并提高开发效率。●领域特定语言(DSL):元编程使得创建领域特定语言成为可能。通过定义自己的语法和语义,我们可以为特定领域提供更易于理解和使用的接口。●框架扩展:许多开源框架都使用元编程来实现其内部机制。通过利用元编程能力,我们可以扩展这些框架的功能,满足我们特定的需求。1.2流程简单来说就是运行时期的一个策略。例如调用一个对象的某一个方法,如果这个方法不存在,编译器也不会报错,而是通过一系列的查找最终确定是调用还是抛出异常,下面就是简化后的流程:1.3使用示例现在有一个类结构如下:packagecom.alvin.learn.testclassTestClass{StringnameIntegerage}1234561.3.1动态添加属性//TestClass本来没有code属性,动态添加一个TestClass.metaClass.code=10deftest=newTestClass()printlntest.code//可以正常修改test.code=2printlntest.code//输出1021234567891.3.2动态添加方法deftest=newTestClass()//1、现在类里面没有testMethod方法,所以执行会报找不到此方法test.testMethod("thisisargs")//2、在TestClass里面重写invokeMethod方法definvokeMethod(Stringname,Objectargs){println("调用了invokeMethod,namename,argsargs")}//3、再次执行1处的调用,会打印//调用了invokeMethod,name:testMethod,args:[thisisargs]//4、在TestClass里面重写methodMissing方法defmethodMissing(Stringname,Objectargs){println("调用了imethodMissing,namename,argsargs")}//5、再次执行1处的调用,会打印//调用了imethodMissing,name:testMethod,args:t[thisisargs]//6、动态创建方法TestClass.metaClass.testMethod={printlnit}deftest=newTestClass()test.testMethod("thisisargs")//打印thisisargs//7、动态添加静态方法TestClass.metaClass.static.testStaticMethod={println'thisisanewstaticmethod'}TestClass.testStaticMethod()//打印thisisanewstaticmethod1234567891011121314151617181920212223242526272829303132331.3.3全局使用以上使用都是临时的,在别的类中使用添加的方法需要重新注入,以下使全局使用示例。classTestManager{staticTestClasscreateTest(Stringname,intage){//调用不存在的方法returnTestClass.createTest(name,age)}}classApplicationManager{staticvoidinit(){//设置全局ExpandoMetaClass.enableGlobally()//创建Person静态方法TestClass.metaClass.static.createTest={Stringname,Integerage->returnnewTestClass(name:name,age:age)}}}classMain{publicstaticvoidmain(String[]args){println'应用程序正在启动'//初始化ApplicationManager.init()println'应用程序初始化完成'deftest=TestManager.createTest("张三",20)printlntest.name+"----"+test.age}}12345678910111213141516171819202122232425262728293031321.3.4动态创建类//创建一个新的类defmyClass=this.class.classLoader.parseClass("""classMyDynamicClass{StringnameMyDynamicClass(Stringname){this.name=name}}""")//使用MetaClass为类添加方法MyDynamicClass.metaClass.sayHello={->"Hello,mynameis${delegate.name}"}//实例化对象并调用新方法defobj=newMyDynamicClass('Groovy')printlnobj.sayHello()12345678910111213141516171819
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-13 13:35 , Processed in 1.044742 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表