Перейти к основному содержимому

Введение в трейты в Groovy

· 6 мин. чтения

1. Обзор

В этом руководстве мы рассмотрим концепцию трейтов в Groovy . Они были представлены в выпуске Groovy 2.3.

2. Что такое черты?

Трейты — это повторно используемые компоненты, представляющие собой набор методов или поведений, которые мы можем использовать для расширения функциональности нескольких классов.

По этой причине они считаются интерфейсами, несущими как реализации по умолчанию, так и состояние. Все черты определяются с помощью ключевого слова trait .

3. Методы

Объявление метода в трейте аналогично объявлению любого обычного метода в классе. Однако мы не можем объявлять защищенные или закрытые для пакета методы в типаже .

Давайте посмотрим, как реализованы публичные и приватные методы.

3.1. Публичные методы

Для начала мы рассмотрим, как общедоступные методы реализуются в трейте.

Давайте создадим трейт с именем UserTrait и общедоступный метод sayHello :

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

После этого мы создадим класс Employee , реализующий UserTrait :

class Employee implements UserTrait {}

Теперь давайте создадим тест, чтобы убедиться, что экземпляр Employee может получить доступ к методу sayHello UserTrait :

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. Частные методы

Мы также можем создать приватный метод в трейте и ссылаться на него в другом публичном методе.

Давайте посмотрим на реализацию кода в 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.foreach.traits.Employee.greetingMessage()"
+ " is applicable for argument types: () values: []"
}

В трейте закрытый метод может быть необходим для любой реализации, которая не должна переопределяться каким-либо классом, хотя и требуется для других общедоступных методов.

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

Черты не поддерживают защищенные и частные области.

4. это ключевое слово

Поведение ключевого слова this похоже на поведение в Java. Мы можем рассматривать черту как суперкласс .

Например, мы создадим метод, который возвращает this в трейте :

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

5. Интерфейсы

Трейт также может реализовывать интерфейсы , как это делают обычные классы.

Давайте создадим интерфейс и реализуем его в трейте :

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

Теперь давайте реализуем абстрактный метод интерфейса в классе реализации:

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

6. Свойства

Мы можем добавлять свойства к трейту так же, как и к любому обычному классу:

trait UserTrait implements Human { 
String email
String address
}

7. Расширение признаков

Подобно обычному классу Groovy , трейт может расширять другой трейт с помощью ключевого слова extends :

trait WheelTrait {
int noOfWheels
}

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

class Car implements VehicleTrait {}

Мы также можем расширить несколько трейтов с помощью предложения о реализации :

trait AddressTrait {                                      
String residentialAddress
}

trait EmailTrait {
String email
}

trait Person implements AddressTrait, EmailTrait {}

8. Множественные конфликты наследования

Когда класс реализует два или более трейтов, имеющих методы с одинаковой сигнатурой, нам нужно знать, как разрешать конфликты. Давайте посмотрим, как Groovy разрешает такие конфликты по умолчанию, а также как мы можем переопределить разрешение по умолчанию.

8.1. Разрешение конфликтов по умолчанию

По умолчанию будет выбран метод из последнего объявленного трейта в предложении Implements .

Следовательно, трейты помогают нам реализовать множественное наследование, не сталкиваясь с проблемой бриллианта .

Во-первых, давайте создадим два трейта с методом, имеющим одинаковую сигнатуру:

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

Теперь давайте переопределим разрешение конфликтов множественного наследования по умолчанию в нашем классе Dog с помощью ключевого слова super :

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

9. Реализация трейтов во время выполнения

Чтобы динамически реализовать трейт , мы можем использовать ключевое слово as для приведения объекта к трейту во время выполнения .

Например, давайте создадим AnimalTrait с помощью метода basicBehavior :

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

Чтобы реализовать сразу несколько трейтов, мы можем использовать метод withTraits вместо ключевого слова as :

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

10. Заключение

В этой статье мы увидели, как создавать трейты в Groovy, и изучили некоторые из их полезных функций.

Трейт — это действительно эффективный способ добавления общих реализаций и функций в наши классы. Кроме того, это позволяет нам свести к минимуму избыточный код и упрощает обслуживание кода.

Как обычно, реализации кода и модульные тесты для этой статьи доступны на GitHub .