技术开发 频道

Groovy 1.6的新特性

  @Category与@Mixin

  如果使用Groovy有一段时间了,那么你肯定知道Category这个概念。它是继承现有类型(甚至可以继承JDK中的final类以及第三方类库)并增加方法的一种机制。该技术还可用来编写领域特定语言。先来看看下面这个示例:

1 final class Distance {
2
3   def number
4
5   String toString() { "${number}m" }
6
7   }
8
9   class NumberCategory {
10
11   static Distance getMeters(Number self) {
12
13   new Distance(number: self)
14
15   }
16
17   }
18
19   use(NumberCategory) {
20
21   def dist = 300.meters
22
23   assert dist instanceof Distance
24
25   assert dist.toString() == "300m"
26
27   }

  这里我们假想了一个简单的Distance类,它可能来自于第三方,他们将该类声明为final以防止其他人继承该类。但借助于Groovy Category,我们可以用额外的方法装饰Distance类。这里我们通过装饰Number类型为number增加一个getMeters()方法。通过为number增加一个getter,你可以使用Groovy优雅的属性语法来引用number。这样就不必使用300.getMeters()了,只需使用300.meters即可。

  这种Category系统和记号不利的一面是:要想给其它类型增加实例方法,我们需要创建static方法,而且该方法的第一个参数代表了我们要影响的类型。其他参数就是方法所使用的一般参数了。因此这与加到Distance中的一般方法相比不那么直观,我们是否应该访问源代码以对其增强。现在就是@Category注解发挥作用的时候了,它会将拥有(要增加的)实例方法的类转换为Groovy category:

1 @Category(Number)
2
3   class NumberCategory {
4
5   Distance getMeters() {
6
7   new Distance(number: this)
8
9   }
10
11   }

  无需将方法声明为static的,同时这里所用的this实际上是category所操纵的number而不是我们所创建的category实例的this。然后可以继续使用use(Category) {}构造方法来应用category。这里需要注意的是这些category一次只能用在一种类型上,这与传统的category不同(可以应用到多种类型上)。

  现在通过搭配@Category与@Mixin,我们可以将多种行为混合到一个类中,类似于多继承:       

1 @Category(Vehicle) class FlyingAbility {
2
3   def fly() { "I'm the ${name} and I fly!" }
4
5   }
6
7   @Category(Vehicle) class DivingAbility {
8
9   def dive() { "I'm the ${name} and I dive!" }
10
11   }
12
13   interface Vehicle {
14
15   String getName()
16
17   }
18
19   @Mixin(DivingAbility)
20
21   class Submarine implements Vehicle {
22
23   String getName() { "Yellow Submarine" }
24
25   }
26
27   @Mixin(FlyingAbility)
28
29   class Plane implements Vehicle {
30
31   String getName() { "Concorde" }
32
33   }
34
35   @Mixin([DivingAbility, FlyingAbility])
36
37   class JamesBondVehicle implements Vehicle {
38
39   String getName() { "James Bond's vehicle" }
40
41   }
42
43   assert new Plane().fly() ==
44
45   "I'm the Concorde and I fly!"
46
47   assert new Submarine().dive() ==
48
49   "I'm the Yellow Submarine and I dive!"
50
51   assert new JamesBondVehicle().fly() ==
52
53   "I'm the James Bond's vehicle and I fly!"
54
55   assert new JamesBondVehicle().dive() ==
56
57   "I'm the James Bond's vehicle and I dive!"
58

  我们并没有继承多个接口并将相同的行为注入到每个子类中,相反我们将category混合到类中。示例中James Bond(007)的交通工具通过掺元获得了飞翔和潜水的能力。

  值得注意的是,不像@Delegate可以将接口注入到声明代理的类中,@Mixin是在运行时实现掺元——在后面的元编程增强一节中将会深入探讨这一点。

1
相关文章