概述
文章目录
- 基础
- 基本类型
- javaBean
- 操作符
- 实现接口
- trait关键字
- 代码生成注解
- 陷阱
基础
-
默认导入: Groovy自动导入以下包或者类
java.io.* java.lang.* java.math.BigDecimal java.math.BigInteger java.net.* java.util.* groovy.lang.* groovy.util.*
-
groovy中分号(;)是可选的,一行中有多条语句时,分号还是必须的。groovy扩展了JDK,这些扩展称作GDK(或者groovy GDK)
-
循环简化
public class HellWorld{ public static void main(String[] args) { //1 常规java写法 for(int i=0;i<3;i++){ System.out.println("Hello World " + i) } //2 groovy 中的Range对象 1等价于2 for(i in 0..2){println("Hello World $i")} /**3 groovy向java.lang.Integer添加了一个upto方法,可用于迭代 * $it 代表进行循环时的索引值 */ 0.upto(2){ println("Hello World $it")} //4 1234等价 0.times { println("Hello World $it") } //5 0123 0.step(4,1){print "$it"} def x = 0 for ( i in [0, 1, 2, 3, 4] ) { x += i } def array = (0..4).toArray() x = 0 for ( i in array ) { x += i } } }
-
空判断:经常需要检查引用是否为空(null),使用?.操作符可以简化,只有当引用不为null的时候才会调用指定的方法或属性.在空引用上调用?.不会发生空指针异常
String str = null print str?.hello
-
groovy不强制我们捕获非检查异常
def class ExceptionHandle { public static void main(String[] args) { openFile("aa") } def static openFile(String file) { new FileInputStream(file) } }
-
静态方法内可以使用this来引用Class对象,static方法返回this可以链式调用
public class ClassTest { def static hello(String name){ println(name) return this } def static world(String name){ println(name) return this } public static void main(String[] args) { //hello //hello ClassTest.hello("hello").hello("hello") } }
-
灵活初始化与具名参数:在构造对象时,可以简单地以逗号分隔的名值对来给出属性值。如果类有一个无参数构造器,该操作会在构造器之后执行。也可以设计自己的方法,是其接受具名参数,要利用这一特性,需要把第一个形参定义为Map
def class Dog{ // def age,height,width def println(age,height,width){ println("打印 age:$age,height:$height,width:$width ") } public static void main(String[] args) { //groovy 为我们创建了灵活的构造器 def Dog dog = new Dog(age:11,height: 100,width: 20) //输出: 11,20,100 println("$dog.age,$dog.width,$dog.height") //输出:[age:11, height:200, width:30] dog.println(age:11,height:200,width:30) //可以修改参数的顺序,输出:[width:30, height:200, age:11] dog.println(width:30,height:200,age:11) //输出: 打印 age:[width:30, height:200, age:11],height:10,width:15 //如果发送的实参个数多于方法的参数的个数,而且多出的实参是明值对,那么 //groovy会假设方法的第一个形参是一个Map,然后将实参列表中的所有名值对组织 //到一起,作为第一个形参的值 dog.println(width:30,height:200,age:11,10,15) //输出: 打印 age:11,height:200,width:30 //如果想使用具名参数,最好只接受一个Map形参,而不要混用不同的形参 dog.println(11,200,30) } }
-
可选形参:定义可选形参,只需要在形参列表中给它赋上一个值,可以为最后一个形参提供零个或多个值
def class Log { //定义可选形参,只需要在形参列表中给它赋上一个值 def static log(x,base=10){ println(x+base) } //可以为最后一个形参提供零个或多个值 def static log2(x,String[] base){ println("$x,$base"); } def static log3(x,int[] base){ int sum = x; for(int i=0;i<base.length;i++){ sum = sum+base[i] } println("$sum") } public static void main(String[] args) { //20 Log.log(10) //30 Log.log(10,20) //10,[1, 2, 3] Log.log2(10,"1","2","3") //26 Log.log3(20,1,2,3) } }
-
多赋值
def class Student{ def static name(fullName){ fullName.split(" ") } public static void main(String[] args) { //返回一个数组 def(firstName,lastName)=Student.name("Tom Bond") //要将结果设置到两个变量中,不必创建临时变量并编写多条赋值语句 //Tom,Bond println("$firstName,$lastName") //可以使用该特性交换两个变量,无需创建中间变量来保存被交换的值 def age = 11,age2=12 (age,age2)=[age2,age] //age:12,age2:11 println("age:$age,age2:$age2") //变量与值不匹配,groovy会将多余的变量设置为null,左边只有两个变量 //所以和面的CC和DD被丢弃 def(String A,String B)=["AA","BB","CC","DD"] //AA,BB println("$A,$B") //AA,BB,null .如果多余的变量不能设置为null,则会抛出异常 def(String AA,String BB,String CC)=["AA","BB"] println("$AA,$BB,$CC") } }
-
总结
- return语句总是可选的
- 分号分隔语句是可选的
- 方法和类默认是public的
- ?. 操作符只有对象引用不为空时才会分派调用
- 可以使用具名参数初始化javaBean
- Groovy不强迫我们捕获不关心的异常,异常会传递给调用者
- 静态方法内可以使用this来引用Class对象,static方法返回this可以链式调用,如下:
基本类型
-
Groovy
支持整数和浮点数,整数是Integer
的实例,浮点数是BigDecimal
的实例.两个整数的除法运算会得到一个浮点数,即使结果可能是一个整数,比如6/3=2.0而不是2。如果想获得两个整型值相除的整数部分,必须调用**intdiv()**方法class Num { public static final void main(String[] args){ //2 println 4/2 //2.5 println 5/2 //2 println 5.intdiv(2) //2 println 4.intdiv(2) //1 println 5%2 } }
-
布尔求值
def class BooleanValueTest { int age = 10; boolean asBoolean() { age > 10 ? true : false } public static void main(String[] args) { /** * groovy尝试推断,如果引用为null则为false * 非null的值视作true。如果引用不为空,还与对象 * 的类型有关。如果一个集合比如ArrayList,groovy * 会检查集合是否为空,即使对象不为空,集合为空,此时 * 也是返回false.集合类是唯一受到特殊对待的 */ def str = "hell0" //true if (str) { println(true) } else { println(false) } //false def aa = [] if (aa) { println(true) } else { println(false) } println null == false // 输出false println null == true // 输出false //除了使用groovy内建的布尔求值约定,在自己的类中可通过实现 // asBoolean()方法来编写自己的布尔转换 BooleanValueTest booleanValueTest = new BooleanValueTest(); booleanValueTest.age = 10 //true println !booleanValueTest booleanValueTest.age = 20 //false println !booleanValueTest } }
javaBean
-
javaBean,对于Groovy而言,不区分public、private、protected。groovy默认会为属性创建一个setter和getter方法,对于final属性,groovy只会创建一个getter不会创建setter方法,如果修改final属性将导致异常
def class Person { //属性设置为只读,如果修改final将导致异常 final name ="jannal" int age //可以把字段标记为private但是groovy不遵守这点 //如果想把字段私有,必须实现一个拒绝任何修改的setter private int sex=40; private void setSex(sex){ throw new IllegalAccessException("不允许修改sex"); } public static void main(String[] args) { Person person = new Person() //相当于setAge person.age = 18 //相当于getAge println(person.age) println(person.name) println(person.sex) //.class不能用于Map和Builder等类型,为了保险起见 //使用getClass().getName() println(person.class.name); println(person.getClass().getName()) } }
操作符
- Groovy支持操作符重载,可以巧妙地应用这一点来创建DSL。Groovy通过每个操作符映射一个标准的方法来支持操作符重载。
实现接口
-
Groovy没有强制实现接口中的所有方法:可以只实现自己关心的方法,而不需要考虑其他方法。当在单元测试中通过实现接口来模拟某些行为时,这项技术非常有用
interface A{ void a() void b() void c() } interface B{ void c() } def class AA{ def aa(A a){ a.a() } def bb(A b){ b.b() } def ab(A ab){ ab.a() ab.b() } def dd(def m){ m.c() } public static void main(String[] args) { def AA aa = new AA() //输出:A的a方法实现 //groovy并没有强制实现所有方法,可以只定义自己关心的方法,而不需要 //考虑其他方法。借助as操作符相当于实现了接口 aa.aa({println("A的a方法实现")} as A) //如果要实现多个方法,需要创建一个映射,以每个方法的名字作为key,方法 //对应的代码作为value,使用:分隔 def implMethods=[ a:{println("A的a方法实现")}, b:{println("A的b方法实现")} ] //输出 //A的a方法实现 //A的b方法实现 aa.ab(implMethods as A ) /** *如果知道所实现接口的名字使用as操作符即可,但是如果应用要求的行为是动态的 * 而且只有在运行的时候才知道接口的名字,可以使用asType */ aa.dd({println "cc-B"}.asType(Class.forName("com.jannal.one.B"))) aa.dd({println "cc-A"}.asType(Class.forName("com.jannal.one.A"))) } }
trait关键字
-
trait修饰的接口可以被看作是承载默认实现和状态的接口。一个类可以同时拥有多个trait而不需要使用多重继承。trait中的this关键字代表了trait的实现的实例
/** * 游泳的能力 */ trait SwimmingAbility { def swim() { println "swimming.." } } /** * 飞行的能力 */ trait FlyingAbility { def fly() { println "flying.." } def fly2() { println "${this.class.name} is flying.." } } /** * 金鱼实现游泳的行为,就具备游泳的能力 */ class Goldfish implements SwimmingAbility { } /** * 鸭子既具备游泳的能力,又具备飞行的能力 */ class Duck implements SwimmingAbility, FlyingAbility { } class TraitTest { public static void main(String[] args) { def goldfish = new Goldfish() goldfish.swim() def duck = new Duck() duck.swim() duck.fly() //输出:com.jannal.trait.Duck is flying.. duck.fly2() } }
-
trait使用extends关键字实现继承
trait Dealer { def getProfit() { println "3000" } def deal() { println "base deal" } } trait CarDealer extends Dealer { def deal() { println "CarDealer deal" } }
-
多实现方法冲突
trait Bike { def drive() { println "Bike drive" } } trait Car { def drive() { println "Car drive" } } /** * 同时实现多个trait,相同的方法会冲突,以最后一个实现为准 * 这里最后一个实现是Bike */ class DrivingThing implements Car, Bike { } class TraitTest2 { public static void main(String[] args) { def drivingThing = new DrivingThing() //输出:Bike drive drivingThing.drive(); } }
代码生成注解
-
Groovy在groovy.transform包和其他的包中提供了很多代码生成注解
-
@Canonical:如果要编写的toString方法只是简单显示以逗号分隔的字段值,可以使用
@Canonical
让编译器来生成,默认会生成所有字段,可以手动排除字段//@Canonical @Canonical(excludes = "sex,age") class CanonicalTest { String name ="jannal" int age = 12 String sex="男" static void main(String[] args) { def test = new CanonicalTest() /** * 不加excludes 输出com.jannal.one.CanonicalTest(jannal, 12, 男) * @Canonical(excludes = "sex,age") * 输出 com.jannal.one.CanonicalTest(jannal) */ println(test) } }
-
@Delegate:委托
class Worker { def work() { println 'work' } def analyze() { println 'worker analyze' } } class Expert { def analyze() { println 'expert analyze' } } /** * 1. 编译时,Groovy会检查Manager类,如果该类中没有被委托类中的方法 * 就把这些方法从被委托类中引入进来。因此它会引入Expert类的analyze()方法 * 而从Worker类中,只会把work()方法引入进来,因为Expert类的analyze()已经出现 * 在Manager类中,所以Worker类中的analyze()会被忽略 * 2. manager中生成的方法如下 * def analyze(){ * expert.analyze() * } * 3. 如果Worker或者Expert中新增方法,只需要重新编译,不需要对Manager修改 */ class Manager{ @Delegate Expert expert = new Expert(); @Delegate Worker worker = new Worker(); public static void main(String[] args) { def manager = new Manager(); //expert analyze manager.analyze() //work manager.work() } } /** * 调整委托顺序,则输出worker analyze而不是expert analyze */ class Manager1{ @Delegate Worker worker = new Worker(); @Delegate Expert expert = new Expert(); public static void main(String[] args) { def manager = new Manager1(); //worker analyze manager.analyze() //work manager.work() } } /** * 手动排除expert的analyze */ class Manager2{ @Delegate Worker worker = new Worker(); @Delegate(excludes = "analyze") Expert expert = new Expert(); public static void main(String[] args) { def manager = new Manager2(); //work manager.work() //worker analyze manager.analyze() } }
-
@Immutable:不可变对象。使用@Immutable标记一个类,Groovy会将其字段标记为final的,并且额外创建了一些方法
/** * groovy提供了一个构造器,其参数顺序与类中定义的字段顺序一样 * groovy还添加了hashCode、equals()、toString */ @Immutable class CreditCard{ String cardNum; int creditLimit; } class ImmutableTest { public static void main(String[] args) { def creditCard = new CreditCard("1234-4567-89",1000) //com.jannal.one.CreditCard(1234-4567-89, 1000) println creditCard.toString() } }
-
@Lazy: 可以把耗时对象的构建推迟到真正需要的时候。既可以在声明的地方直接初始化实例,也可以将创建逻辑包在一个闭包中
class Heavy { def size = 10 Heavy() { println "init $size" } } /** * @Lazy Groovy不仅推迟了创建,还将字段标识为volatile,并确保创建时间是线程安全的 * 实例会在第一次访问这些字段的时候被创建 */ class AsNeeded { def value @Lazy Heavy heavy1 = new Heavy() @Lazy Heavy heavy2 = { new Heavy(size: value) }() AsNeeded() { println("Created AsNeeded") } } class LazyTest { public static void main(String[] args) { /** * Created AsNeeded * init 10 * 10 * 10 * init 10 * 1000 */ def asNeeded = new AsNeeded(value: 1000) println asNeeded.heavy1.size println asNeeded.heavy1.size println asNeeded.heavy2.size } }
陷阱
-
Groovy的==等价于java的equals()
-
Groovy的is()等价于java的==
-
Groovy的映射到equals()这个结论并从总是成立,当且仅当该类没有实现Comparable接口时,才会这样映射。如果实现了Comparable接口,则会映射到该类的compareTo()方法
-
Groovy的类型是可选的,Groovy编译器groovyc大多数情况下不会执行完整的类型检查。他只是进行强制类型转换,然后将其留给运行时处理。
-
如果调用了一个不存在的方法,也不会出现编译错误:groovy.lang.MissingMethodException异常。Groovy编译器可能看上起不够严格,但是对于groovy的动态和元变成等强项而言,这种行为是必要的。在2.x版本中,我们可以关闭这种动态类型特性,并增强编译时类型检查。
-
java中方法里可以加入代码块。但是在groovy中会感到困惑。Groovy编译器会错误地认为我们是要定义一个闭包,并给出编译错误。在Groovy中,方法内不能有任何这样的代码
public void method(){ System.out.println("a"); { System.out.println("b"); } }
-
闭包与匿名内部类的冲突
- groovy的闭包是使用{}定义的,而定义匿名内部类也是使用{}
def class Car { Car(carBlock){ println "hi car" carBlock() } public static void main(String[] args) { /** * 这个例子中我们调用构造器,接受一个闭包作为参数 * 但是groovy认为我们是要创建一个匿名内部类,因而编译报错 */ //def car = new AnonymousConflict(){println ("i am carBlock") } //要绕开以上的陷阱,必须修改调用方式,将闭包放在构造器调用语句的圆括号内 //也可以在调用时定义闭包,或传递一个引用该闭包的变量 def car =new Car({prinln "i am carBlock"}) def carBlock = {prinln "i am carBlock2"} def car1 = new Car(carBlock) } }
-
数组:在Groovy中,{…}块是为闭包而保留的,所以不能通过如下方式定义数组
int[] array = {1,2,3} 或者(在java中正常,在groovy中编译错误) int[] arr = new int[]{1,2,3,4,5}; //groovy如下方式定义基本类型数组 int[] arr=[1,2,3,4,5] //输出: class is [I //JVM用[I表示int[] println "class is "+arr.getClass().name //使用as操作符创建数组 def arr = [1,2,3,4,5] as int[]
-
方法调用
- 在Java中,则是根据声明的类型,在编译时选择方法。
public class MethodTest { public void run(String str) { System.out.println(1); } public void run(Object str) { System.out.println(2); } public static void main(String[] args) { MethodTest methodTest = new MethodTest(); Object o = "Object"; //2 methodTest.run(o); Object str = "Object"; //2 methodTest.run(str); } }
- 在Groovy中,将在运行时选择将被调用的方法,即将基于运行时参数的类型来选择方法
class MethodTest { def run(String str) { System.out.println(1); } def run(Object str) { System.out.println(2); } public static void main(String[] args) { com.jannal.MethodTest methodTest = new com.jannal.MethodTest(); Object o = "Object"; //1 methodTest.run(o); Object str = "Object"; //1 methodTest.run(str); } }
-
在Groovy中,在字段上省略修饰符不会像Java中一样变成package-private字段。可以通过使用
@PackageScope
注释来创建一个package-private字段。 -
Groovy使用Objects来做每一件事情,包括基本类型
class Foo { static int i } println Foo.class.getDeclaredField('i').type == int.class println Foo.i.class != int.class && Foo.i.class == Integer.class
最后
以上就是香蕉发卡为你收集整理的Groovy笔记(一)之基础基础陷阱的全部内容,希望文章能够帮你解决Groovy笔记(一)之基础基础陷阱所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复