我是靠谱客的博主 香蕉发卡,最近开发中收集的这篇文章主要介绍Groovy笔记(一)之基础基础陷阱,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 基础
    • 基本类型
    • javaBean
    • 操作符
    • 实现接口
    • trait关键字
    • 代码生成注解
  • 陷阱

基础

  1. 默认导入: Groovy自动导入以下包或者类

        java.io.*
        java.lang.*
        java.math.BigDecimal
        java.math.BigInteger
        java.net.*
        java.util.*
        groovy.lang.*
        groovy.util.*
    
  2. groovy中分号(;)是可选的,一行中有多条语句时,分号还是必须的。groovy扩展了JDK,这些扩展称作GDK(或者groovy GDK)

  3. 循环简化

        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
                }
    
        
            }
        }    
    
  4. 空判断:经常需要检查引用是否为空(null),使用?.操作符可以简化,只有当引用不为null的时候才会调用指定的方法或属性.在空引用上调用?.不会发生空指针异常

        String str = null
        print str?.hello
    
  5. groovy不强制我们捕获非检查异常

        def class ExceptionHandle {
        
            public static void main(String[] args) {
                openFile("aa")
            }
        
            def static openFile(String file) {
                new FileInputStream(file)
            }
        }
    
  6. 静态方法内可以使用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")
            }
        }
    
  7. 灵活初始化与具名参数:在构造对象时,可以简单地以逗号分隔的名值对来给出属性值。如果类有一个无参数构造器,该操作会在构造器之后执行。也可以设计自己的方法,是其接受具名参数,要利用这一特性,需要把第一个形参定义为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)
            }
        }
    
  8. 可选形参:定义可选形参,只需要在形参列表中给它赋上一个值,可以为最后一个形参提供零个或多个值

        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)
        
            }
        }
    
  9. 多赋值

        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")
            }
        }
    
  10. 总结

    • return语句总是可选的
    • 分号分隔语句是可选的
    • 方法和类默认是public的
    • ?. 操作符只有对象引用不为空时才会分派调用
    • 可以使用具名参数初始化javaBean
    • Groovy不强迫我们捕获不关心的异常,异常会传递给调用者
    • 静态方法内可以使用this来引用Class对象,static方法返回this可以链式调用,如下:

基本类型

  1. 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
            }
        }
    
    
  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

  1. 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())
            }
        }
    

操作符

  1. Groovy支持操作符重载,可以巧妙地应用这一点来创建DSL。Groovy通过每个操作符映射一个标准的方法来支持操作符重载。

实现接口

  1. 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关键字

  1. 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()
            }
        }
        
    
  2. trait使用extends关键字实现继承

        trait Dealer {
            def getProfit() {
                println "3000"
            }
        
            def deal() {
                println "base deal"
            }
        }
        
        trait CarDealer extends Dealer {
            def deal() {
                println "CarDealer deal"
            }
        }
    
  3. 多实现方法冲突

        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();
        
            }
        }
    
    

代码生成注解

  1. Groovy在groovy.transform包和其他的包中提供了很多代码生成注解

  2. @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)
            }
        }
    
  3. @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()
        
            }
        }
        
    
    
  4. @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()
            }
        }
    
    
    
  5. @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
            }
        }
    
    

陷阱

  1. Groovy的==等价于java的equals()

  2. Groovy的is()等价于java的==

  3. Groovy的映射到equals()这个结论并从总是成立,当且仅当该类没有实现Comparable接口时,才会这样映射。如果实现了Comparable接口,则会映射到该类的compareTo()方法

  4. Groovy的类型是可选的,Groovy编译器groovyc大多数情况下不会执行完整的类型检查。他只是进行强制类型转换,然后将其留给运行时处理。

  5. 如果调用了一个不存在的方法,也不会出现编译错误:groovy.lang.MissingMethodException异常。Groovy编译器可能看上起不够严格,但是对于groovy的动态和元变成等强项而言,这种行为是必要的。在2.x版本中,我们可以关闭这种动态类型特性,并增强编译时类型检查。

  6. java中方法里可以加入代码块。但是在groovy中会感到困惑。Groovy编译器会错误地认为我们是要定义一个闭包,并给出编译错误。在Groovy中,方法内不能有任何这样的代码

        public void method(){
            System.out.println("a");
            {
                System.out.println("b");
            }
        }
    
  7. 闭包与匿名内部类的冲突

    • 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)
    
    
        }
    }
    
    
  8. 数组:在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[]
    
  9. 方法调用

    • 在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);
            }
        }
    
  10. 在Groovy中,在字段上省略修饰符不会像Java中一样变成package-private字段。可以通过使用@PackageScope注释来创建一个package-private字段。

  11. 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笔记(一)之基础基础陷阱所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部