一般情况下当我们访问或修改不存在的属性、调用不存在的方法会抛出异常,而在Groovy中则可以通过实现相应的Hook Method钩子方法以避免异常发生
属性缺失
Groovy在MP元编程方面提供了丰富的特性,其中之一就是在访问、修改不存在的属性时,提供了一个钩子方法。下面即是一个简单的Groovy示例,当我们访问、修改Tiger类不存在的属性,即会抛出相应的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| 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 }
|
测试结果如下所示
而由于Groovy为属性缺失这一场景提供了相应了钩子方法:propertyMissing、$static_propertyMissing。以避免直接抛出相应的异常,示例代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| 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
def propertyMissing(String propertyName) { return "Not ${propertyName} Property For Get!" }
void propertyMissing(String propertyName, Object args) { println "Not ${propertyName} Property! For Set, args: ${args}" }
static def $static_propertyMissing(String propertyName) { return "Not ${propertyName} Static Property For Get!" }
static void $static_propertyMissing(String propertyName, Object args) { println "Not ${propertyName} Static Property For Set, args: ${args}" } }
|
测试结果如下所示,符合预期
方法缺失
当我们调用一个不存在的方式时,即会抛出异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 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 }
|
测试结果如下,符合预期
对于非静态方法而言,我们可以实现methodMissing方法以避免抛出异常。示例代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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
def methodMissing(String methodName, Object args) { return "[DOG] ==>> methodName: ${methodName}, args: ${args}" } }
|
对于静态方法而言,我们可以实现$static_methodMissing方法以避免抛出异常。示例代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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 def $static_methodMissing(String methodName, Object args) { return "Missing Static Method: $methodName" } }
|
与此同时,我们还可以通过invokeMethod方法进行动态选择的方法调用,示例代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| 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~" }
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 } }
|
参考文献
- Groovy In Action · 2nd Edition Dierk König、Guillaume Laforge著