Contents

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!"
    }
}

之后,我们将创建一个实现 UserTraitEmployee类:

class Employee implements UserTrait {}

现在,让我们创建一个测试来验证Employee实例是否可以访问UserTraitsayHello方法:

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!!"
    }
}

特征不支持protectedprivate范围。

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