Groovy之Hook Method钩子方法

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

abstract.png

属性缺失

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
}

测试结果如下所示

figure 1.jpeg

而由于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

/**
* 实现访问不存在的属性的钩子函数
* @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

方法缺失

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

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
}

测试结果如下,符合预期

figure 3.jpeg

对于非静态方法而言,我们可以实现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

/**
* 实现方法缺失的钩子函数
* @param methodName
* @param args
* @return
*/
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_methodMissing, 以避免抛出MissingMethodException异常
* @param methodName
* @param args
* @return
*/
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~"
}

/**
* 实现方法缺失的钩子函数
* @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著
0%