An Introduction to Traits in Groovy – Groovy中的Traits简介

最后修改: 2019年 2月 21日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll explore the concept of traits in Groovy. They were introduced in the Groovy 2.3 release.

在本教程中,我们将探讨Groovy中特质的概念。它们是在Groovy 2.3版本中引入的。

2. What Are Traits?

2.什么是特质?

Traits are reusable components representing a set of methods or behaviors that we can use to extend the functionality of multiple classes.

特质是可重复使用的组件,代表一组方法或行为,我们可以用它来扩展多个类的功能。

For this reason, they’re considered as interfaces, carrying both default implementations and state. All traits are defined using the trait keyword.

由于这个原因,它们被认为是接口,同时携带默认的实现和状态。所有特性都是用trait关键字定义的。

3. Methods

3.方法

Declaring a method in a trait is similar to declaring any regular method in a class. However, we cannot declare protected or package-private methods in a trait.

trait中声明一个方法与在类中声明任何常规方法类似。然而,我们不能在trait中声明受保护的或打包的私有方法。

Let’s see how public and private methods are implemented.

让我们看看公有和私有方法是如何实现的。

3.1. Public Methods

3.1.公共方法

To start, we’ll explore how public methods are implemented in a trait.

首先,我们将探讨public方法如何在trait中实现。公有方法

Let’s create a trait named UserTrait and a public sayHello method:

让我们创建一个名为traitUserTrait和一个publicsayHello方法。

trait UserTrait {
    String sayHello() {
        return "Hello!"
    }
}

After that, we’ll create an Employee class, which implements UserTrait:

之后,我们将创建一个Employee类,它实现了UserTrait

class Employee implements UserTrait {}

Now, let’s create a test to verify that an Employee instance can access the sayHello method of the 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. Private Methods

3.2.私人方法

We can also create a private method in a trait and refer to it in another public method.

我们也可以在一个trait中创建一个private方法,并在另一个public方法中引用它。

Let’s see the code implementation in the UserTrait:

让我们看看UserTrait中的代码实现:

private String greetingMessage() {
    return 'Hello, from a private method!'
}
    
String greet() {
    def msg = greetingMessage()
    println msg
    return msg
}

Note that if we access the private method in the implementation class, it will throw a MissingMethodException:

注意,如果我们访问实现类中的private 方法,它将抛出一个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.baeldung.traits.Employee.greetingMessage()"
          + " is applicable for argument types: () values: []"
}

In a trait, a private method may be essential for any implementation that should not be overridden by any class, though required by other public methods.

在一个属性中,一个私有方法可能是任何实现所必需的,它不应被任何类所重写,尽管其他公共方法需要。

3.3. Abstract Methods

3.3.抽象方法

A trait can also contain abstract methods that can then be implemented in another class:

一个trait也可以包含abstract方法,然后可以在另一个类中实现。

trait UserTrait {
    abstract String name()
    
    String showName() {
       return "Hello, ${name()}!"
    }
}
class Employee implements UserTrait {
    String name() {
        return 'Bob'
    }
}

3.4. Overriding Default Methods

3.4.重写默认方法

Usually, a trait contains default implementations of its public methods, but we can override them in the implementation class:

通常,一个trait包含其公共方法的默认实现,但我们可以在实现类中重写它们。

trait SpeakingTrait {
    String speak() {
        return "Speaking!!"
    }
}
class Dog implements SpeakingTrait {
    String speak() {
        return "Bow Bow!!"
    }
}

Traits do not support protected and private scopes.

Traits不支持protectedprivate作用域。

4. this Keyword

4.这个关键词

The behavior of the this keyword is similar to that in Java. We can consider the trait as a super class.

this关键字的行为与Java中的行为类似。我们可以把trait看作一个super类。

For instance, we’ll create a method which returns this in a trait:

例如,我们将创建一个方法,在一个trait中返回this

trait UserTrait {
    def self() {
        return this 
    }
}

5. Interfaces

5.接口

A trait can also implement interfaces, just like regular classes do.

trait也可以实现接口,就像普通类那样。

Let’s create an interface and implement it in a trait:

让我们创建一个接口并在trait中实现它。

interface Human {
    String lastName()
}
trait UserTrait implements Human {
    String showLastName() {
        return "Hello, ${lastName()}!"
    }
}

Now, let’s implement the abstract method of the interface in the implementation class:

现在,让我们在实现类中实现interfaceabstract方法。

class Employee implements UserTrait {
    String lastName() {
        return "Marley"
    }
}

6. Properties

6.财产

We can add properties to a trait just like we would in any regular class:

我们可以向trait添加属性,就像我们在任何常规类中一样:

trait UserTrait implements Human { 
    String email
    String address
}

7. Extending Traits

7.扩展特征

Similar to a regular Groovy class, a trait may extend another trait using the extends keyword:

与普通的Groovy class类似,一个trait可以使用extends关键字扩展另一个trait

trait WheelTrait {
    int noOfWheels
}

trait VehicleTrait extends WheelTrait {
    String showWheels() {
        return "Num of Wheels $noOfWheels" 
    } 
}

class Car implements VehicleTrait {}

We can also extend multiple traits with the implements clause:

我们还可以通过implements子句扩展多个特征:

trait AddressTrait {                                      
    String residentialAddress
}

trait EmailTrait {                                    
    String email
}

trait Person implements AddressTrait, EmailTrait {}

8. Multiple Inheritance Conflicts

8.多重继承冲突

When a class implements two or more traits that have methods with the same signature, we need to know how to resolve the conflicts. Let’s look at how Groovy resolves such conflicts by default, as well as a way that we can override the default resolution.

当一个类实现了两个或多个具有相同签名的方法的特性时,我们需要知道如何解决这些冲突。让我们看看Groovy是如何默认解决这种冲突的,以及我们可以覆盖默认解决的方法。

8.1. Default Conflict Resolution

8.1.默认的冲突解决方案

By default, the method from the last declared trait in the implements clause will be picked up.

默认情况下,implements子句中最后声明的trait的方法将被拾取

Therefore, traits help us to implement multiple inheritances without encountering the Diamond Problem.

因此,特质帮助我们实现多个继承,而不会遇到钻石问题

First, let’s create two traits with a method having the same signature:

首先,让我们创建两个具有相同签名的方法的特质。

trait WalkingTrait {
    String basicAbility() {
        return "Walking!!"
    }
}

trait SpeakingTrait {
    String basicAbility() {
        return "Speaking!!"
    }
}

Next, let’s write a class that implements both traits:

接下来,让我们写一个实现这两个特征的类。

class Dog implements WalkingTrait, SpeakingTrait {}

Because SpeakingTrait is declared last, its basicAbility method implementation would be picked up by default in the Dog class.

因为SpeakingTrait是最后声明的,它的basicAbility方法实现将被默认在Dog类中拾取。

8.2. Explicit Conflict Resolution

8.2.明确的冲突解决

Now, if we don’t want to simply take the default conflict resolution provided by the language, we can override it by explicitly choosing which method to call using the trait.super.method reference.

现在,如果我们不想简单地采用语言提供的默认冲突解决方法,我们可以通过明确地选择使用trait.super.method引用来重写它。

For instance, let’s add another method with the same signature to our two traits:

例如,让我们为我们的两个特征添加一个具有相同签名的方法。

String speakAndWalk() {
    return "Walk and speak!!"
}
String speakAndWalk() {
    return "Speak and walk!!"
}

Now, let’s override the default resolution of multiple inheritance conflicts in our Dog class using the super keyword:

现在,让我们使用super关键字在我们的Dog类中覆盖多个继承冲突的默认解决方案。

class Dog implements WalkingTrait, SpeakingTrait {
    String speakAndWalk() {
        WalkingTrait.super.speakAndWalk()
    }
}

9. Implementing Traits at Runtime

9.在运行时实现特质

To implement a trait dynamically, we can use the as keyword to coerce an object to a trait at runtime.

为了动态地实现trait,我们可以使用as关键字,在运行时将一个对象胁迫为trait

For instance, let’s create an AnimalTrait with the basicBehavior method:

例如,让我们用basicBehavior方法创建一个AnimalTrait

trait AnimalTrait {
    String basicBehavior() {
        return "Animalistic!!"
    }
}

To implement several traits at once, we can use the withTraits method instead of the as keyword:

要同时实现几个特征,我们可以使用withTraits方法而不是as关键字。

def dog = new Dog()
def dogWithTrait = dog.withTraits SpeakingTrait, WalkingTrait, AnimalTrait

10. Conclusion

10.结语

In this article, we’ve seen how to create traits in Groovy and explored some of their useful features.

在这篇文章中,我们已经看到了如何在Groovy中创建traits,并探索了它们的一些有用的功能。

A trait is a really effective way to add common implementations and functionalities throughout our classes. In addition, it allows us to minimize redundant code and makes code maintenance easier.

trait是一种真正有效的方式,可以在我们的整个类中添加通用的实现和功能。此外,它允许我们尽量减少多余的代码,使代码维护更容易。

As usual, the code implementations and unit tests for this article are available over on GitHub.

像往常一样,本文的代码实现和单元测试可在GitHub上获得