Groovy之Hook Method钩子方法

ProjectDaedalus

共 15148字,需浏览 31分钟

 · 2022-06-20

一般情况下当我们访问或修改不存在的属性、调用不存在的方法会抛出异常,而在Groovy中则可以通过实现相应的Hook Method钩子方法以避免异常发生

abstract.png

属性缺失

Groovy在MP元编程方面提供了丰富的特性,其中之一就是在访问、修改不存在的属性时,提供了一个钩子方法。下面即是一个简单的Groovy示例,当我们访问、修改Tiger类不存在的属性,即会抛出相应的异常

class MissPropertyDemo {

    static void testTiger() {
        Tiger tiger = new Tiger(name: "Tony"type: "TIGER")

        assert tiger.name == "Tony"
        assert tiger.type == "TIGER"

        try {
            // 访问不存在的属性
            def age = tiger.age
        } catch (Exception e) {
            assert e instanceof MissingPropertyException
            println("Happen Missing Property Exception #1")
        }

        try {
            // 修改不存在的属性
            tiger.age = 234
        } catch (Exception e) {
            assert e instanceof MissingPropertyException
            println("Happen Missing Property Exception #2")
        }

        try {
            // 访问不存在的静态属性
            def remark = Tiger.remark
        } catch (Exception e) {
            assert e instanceof MissingPropertyException
            println("Happen Missing Property Exception #3")
        }

        try {
            // 修改不存在的静态属性
            Tiger.remark = 234
        } catch (Exception e) {
            assert e instanceof MissingPropertyException
            println("Happen Missing Property Exception #4")
        }
    }

}

class Tiger{
    String name
    String type
}

测试结果如下所示

figure 1.jpeg

而由于Groovy为属性缺失这一场景提供了相应了钩子方法:propertyMissing、$static_propertyMissing。以避免直接抛出相应的异常,示例代码如下所示

class MissPropertyDemo {

    static void testLion() {
        Lion lion = new Lion(name: "Tom"type: "LION")

        assert lion.name == "Tom"
        assert lion.type == "LION"

        // 访问不存在的属性
        def result = lion.age
        assert result == "Not age Property For Get!"
        // 修改不存在的属性
        lion.age = 22

        // 访问不存在的静态属性
        result = Lion.remark
        assert result == "Not remark Static Property For Get!"
        // 修改不存在的静态属性
        Lion.remark = "危险动物"
    }
}

class Lion{
    String name
    String type

    /**
     * 实现访问不存在的属性的钩子函数
     * @param propertyName
     * @return
     */

    def propertyMissing(String propertyName) {
        return  "Not ${propertyName} Property For Get!"
    }

    /**
     * 实现修改不存在的属性的钩子函数
     * @param propertyName
     * @param args
     */

    void propertyMissing(String propertyName, Object args) {
        println "Not ${propertyName} Property! For Set, args: ${args}"
    }

    /**
     * 实现访问不存在的静态属性的钩子函数
     * @param propertyName
     * @return
     */

    static def $static_propertyMissing(String propertyName) {
        return "Not ${propertyName} Static Property For Get!"
    }

    /**
     * 实现修改不存在的静态属性的钩子函数
     * @param propertyName
     * @param args
     */

    static void $static_propertyMissing(String propertyName, Object args) {
        println "Not ${propertyName} Static Property For Set, args: ${args}"
    }
}

测试结果如下所示,符合预期

figure 2.jpeg

方法缺失

当我们调用一个不存在的方式时,即会抛出异常

class MissMethodDemo {
    /**
     * 调用不存在的方法, 抛出异常
     */

    static void testCat() {
        Cat cat = new Cat(name:"Tom"type:"CAT")

        try {
            // 调用实例不存在的方法
            cat.fly()
        } catch (Exception e) {
            assert e instanceof MissingMethodException
            println("Happen Missing Method Exception #1")
        }

        try {
            // 调用不存在的静态方法
            Cat.run()
        } catch (Exception e) {
            assert e instanceof MissingMethodException
            println("Happen Missing Method Exception #2")
        }
    }
}

class Cat {
    String name
    String type
}

测试结果如下,符合预期

figure 3.jpeg

对于非静态方法而言,我们可以实现methodMissing方法以避免抛出异常。示例代码如下所示

class MissMethodDemo {
    /**
     * 调用不存在的方法, 返回默认值
     */

    static void testDog() {
        Dog dog = new Dog(name:"Aaron"type:"DOG")
        // 调用实例不存在的方法
        String msg = dog.fly("5""km")
        assert msg == "[DOG] ==>> methodName: fly, args: [5, km]"
    }
}

class Dog {
    String name
    String type

    /**
     * 实现方法缺失的钩子函数
     * @param methodName
     * @param args
     * @return
     */

    def methodMissing(String methodName, Object args) {
        return "[DOG] ==>> methodName: ${methodName}, args: ${args}"
    }
}

对于静态方法而言,我们可以实现$static_methodMissing方法以避免抛出异常。示例代码如下所示

class MissMethodDemo {
    /**
     * 调用不存在的静态方法
     */

    static void testChicken() {
        assert Chicken.getInfo() == "I'm a CHICKEN"
        assert Chicken.calcPrice() == "Missing Static Method: calcPrice"
    }
}

class Chicken {
    static String type = "CHICKEN"

    static String getInfo() {
        return "I'm a ${type}"
    }

    /**
     * 实现静态方法缺失的钩子函数$static_methodMissing, 以避免抛出MissingMethodException异常
     * @param methodName
     * @param args
     * @return
     */

    static def $static_methodMissing(String methodName, Object args) {
        return "Missing Static Method: $methodName"
    }
}

与此同时,我们还可以通过invokeMethod方法进行动态选择的方法调用,示例代码如下所示

class MissMethodDemo {
    /**
     * 调用不存在的方法, 动态处理
     */

    static void testPig() {
        Pig pig = new Pig(name:"Bob"type:"PIG")

        // 调用实例不存在的方法
        def result1 = pig.swim()
        assert result1 == "Yes, Bob can Swim..."

        // 调用实例不存在的方法
        def result2 = pig.fly()
        assert result2 == "Yes, Bob also can Fly~"

        // 调用实例不存在的方法
        def result3 = pig.eat()
        assert result3 == null
    }
}

class Pig {
    String name
    String type

    String swimmable() {
        return "Yes, ${name} can Swim..."
    }

    String flyable() {
        return "Yes, ${name} also can Fly~"
    }

    /**
     * 实现方法缺失的钩子函数
     * @param methodName
     * @param args
     * @return
     */

    def methodMissing(String methodName, Object args) {
        // 新方法名
        String newMethodName
        switch (methodName) {
            case "swim" : {
                newMethodName = methodName + "mable"
                break
            }
            case "fly" : {
                newMethodName = methodName + "able"
                break
            }
            default : newMethodName = null
        }

        if(newMethodName) {
            def result = this.invokeMethod( newMethodName, null )
            return result
        }
        return null
    }
}

参考文献

  1. Groovy In Action · 2nd Edition   Dierk König、Guillaume Laforge著
浏览 19
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报