Groovy 特征简介
1. 概述
在本教程中,我们将探讨Groovy 中特征的概念。它们是在 Groovy 2.3 版本中引入的。
2. 什么是特征?
特征是可重用的组件,代表一组方法或行为,我们可以使用它们来扩展多个类的功能。
因此,它们被视为接口,同时承载默认实现和状态。所有特征都使用trait关键字定义。
3. 方法
在trait中声明方法类似于在类中声明任何常规方法。但是,我们不能在trait中声明 protected 或 package-private 方法。
让我们看看公共和私有方法是如何实现的。
3.1. 公共方法
首先,我们将探讨如何在trait中实现公共方法。
让我们创建一个名为UserTrait的特征和一个公共sayHello方法:
trait UserTrait {
String sayHello() {
return "Hello!"
}
}
之后,我们将创建一个实现 UserTrait的Employee类:
class Employee implements UserTrait {}
现在,让我们创建一个测试来验证Employee实例是否可以访问UserTrait的 sayHello方法:
def 'Should return msg string when using Employee.sayHello method provided by User trait' () {
when:
def msg = employee.sayHello()
then:
msg
msg instanceof String
assert msg == "Hello!"
}
3.2. 私有方法
我们还可以在trait中创建私有方法,并在另一个公共方法中引用它。
让我们看看UserTrait中的代码实现:
private String greetingMessage() {
return 'Hello, from a private method!'
}
String greet() {
def msg = greetingMessage()
println msg
return msg
}
请注意,如果我们在实现类中访问私有方法,它将抛出MissingMethodException:
def 'Should return MissingMethodException when using Employee.greetingMessage method' () {
when:
def exception
try {
employee.greetingMessage()
} catch(Exception e) {
exception = e
}
then:
exception
exception instanceof groovy.lang.MissingMethodException
assert exception.message == "No signature of method: com.blogdemo.traits.Employee.greetingMessage()"
+ " is applicable for argument types: () values: []"
}
在trait中,私有方法对于任何不应被任何类重写的实现可能是必不可少的,尽管其他公共方法需要。
3.3. 抽象方法
特征还可以包含抽象方法,然后可以在另一个类中实现:
trait UserTrait {
abstract String name()
String showName() {
return "Hello, ${name()}!"
}
}
class Employee implements UserTrait {
String name() {
return 'Bob'
}
}
3.4. 覆盖默认方法
通常,特征包含其公共方法的默认实现,但我们可以在实现类中覆盖它们:
trait SpeakingTrait {
String speak() {
return "Speaking!!"
}
}
class Dog implements SpeakingTrait {
String speak() {
return "Bow Bow!!"
}
}
特征不支持protected和private范围。
4. this关键字
this关键字的行为类似于 Java 中的行为。我们可以将trait视为一个超类。
例如,我们将创建一个在trait中返回this的方法:
trait UserTrait {
def self() {
return this
}
}
5. 接口
一个trait也可以实现接口,就像普通类一样。
让我们创建一个接口并在trait中实现它:
interface Human {
String lastName()
}
trait UserTrait implements Human {
String showLastName() {
return "Hello, ${lastName()}!"
}
}
现在,让我们在实现类中实现接口的抽象方法:
class Employee implements UserTrait {
String lastName() {
return "Marley"
}
}
6. 属性
我们可以像在任何常规类中一样向trait添加属性:
trait UserTrait implements Human {
String email
String address
}
7. 扩展特征
与常规的 Groovy Class类似,一个trait可以使用extends关键字扩展另一个trait :
trait WheelTrait {
int noOfWheels
}
trait VehicleTrait extends WheelTrait {
String showWheels() {
return "Num of Wheels $noOfWheels"
}
}
class Car implements VehicleTrait {}
我们还可以使用implements子句扩展多个特征:
trait AddressTrait {
String residentialAddress
}
trait EmailTrait {
String email
}
trait Person implements AddressTrait, EmailTrait {}
8. 多重继承冲突
当一个类实现两个或多个具有相同签名方法的特征时,我们需要知道如何解决冲突。让我们看看 Groovy 如何默认解决此类冲突,以及我们可以覆盖默认解决方案的方法。
8.1. 默认冲突解决
默认情况下,将选取implements子句中最后声明的trait 中的方法。
因此,特征帮助我们在不遇到钻石问题 的情况下实现多重继承 。
首先,让我们使用具有相同签名的方法创建两个特征:
trait WalkingTrait {
String basicAbility() {
return "Walking!!"
}
}
trait SpeakingTrait {
String basicAbility() {
return "Speaking!!"
}
}
接下来,让我们编写一个实现这两个特征的类:
class Dog implements WalkingTrait, SpeakingTrait {}
因为 SpeakingTrait是最后声明的,所以它的basicAbility方法实现将默认在Dog类中被拾取。
8.2. 明确的冲突解决
现在,如果我们不想简单地采用语言提供的默认冲突解决方案,我们可以通过显式选择使用trait.super调用哪个方法来覆盖它。
例如,让我们为我们的两个特征添加另一个具有相同签名的方法:
String speakAndWalk() {
return "Walk and speak!!"
}
String speakAndWalk() {
return "Speak and walk!!"
}
现在,让我们使用super关键字覆盖Dog类中多重继承冲突的默认解决方案:
class Dog implements WalkingTrait, SpeakingTrait {
String speakAndWalk() {
WalkingTrait.super.speakAndWalk()
}
}
9. 在运行时实现 Traits
要动态地实现一个trait,我们可以 使用 as关键字 在运行时将一个对象强制为一个trait。
例如,让我们使用basicBehavior方法创建一个AnimalTrait:
trait AnimalTrait {
String basicBehavior() {
return "Animalistic!!"
}
}
要一次实现多个特征,我们可以使用withTraits方法而不是 as关键字:
def dog = new Dog()
def dogWithTrait = dog.withTraits SpeakingTrait, WalkingTrait, AnimalTrait