Eine Einführung in die Eigenschaften von Groovy

1. Übersicht

In diesem Tutorial werden wir das Konzept der Merkmale in Groovy untersuchen. Sie wurden in der Version Groovy 2.3 eingeführt.

2. Was sind Eigenschaften?

Merkmale sind wiederverwendbare Komponenten, die eine Reihe von Methoden oder Verhaltensweisen darstellen, mit denen wir die Funktionalität mehrerer Klassen erweitern können.

Aus diesem Grund werden sie als Schnittstellen betrachtet, die sowohl Standardimplementierungen als auch den Status enthalten. Alle Merkmale werden mit dem Schlüsselwort trait definiert .

3. Methoden

Das Deklarieren einer Methode in einem Merkmal ähnelt dem Deklarieren einer regulären Methode in einer Klasse. Wir können jedoch keine geschützten oder paketprivaten Methoden in einem Merkmal deklarieren .

Mal sehen, wie öffentliche und private Methoden implementiert werden.

3.1. Öffentliche Methoden

Zunächst werden wir untersuchen, wie öffentliche Methoden in einem Merkmal implementiert werden .

Lassen Sie uns ein Merkmal namens UserTrait und eine öffentliche sayHello- Methode erstellen :

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

Danach erstellen wir eine Employee- Klasse, die UserTrait implementiert :

class Employee implements UserTrait {}

Erstellen wir nun einen Test, um zu überprüfen, ob eine Employee- Instanz auf die sayHello- Methode des UserTrait zugreifen kann :

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 Methoden

Wir können auch eine private Methode in einem Merkmal erstellen und in einer anderen öffentlichen Methode darauf verweisen .

Sehen wir uns die Code-Implementierung im UserTrait an:

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

Beachten Sie, dass beim Zugriff auf die private Methode in der Implementierungsklasse eine MissingMethodException ausgelöst wird :

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 einem Merkmal kann eine private Methode für jede Implementierung wesentlich sein, die von keiner Klasse überschrieben werden sollte, obwohl dies von anderen öffentlichen Methoden verlangt wird.

3.3. Abstrakte Methoden

Ein Merkmal kann auch abstrakte Methoden enthalten , die dann in einer anderen Klasse implementiert werden können:

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

3.4. Standardmethoden überschreiben

Normalerweise enthält ein Merkmal Standardimplementierungen seiner öffentlichen Methoden, aber wir können sie in der Implementierungsklasse überschreiben:

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

Merkmale unterstützen keine geschützten und privaten Bereiche.

4. dieses Schlüsselwort

Das Verhalten dieses Schlüsselworts ähnelt dem in Java. Wir können das Merkmal als eine Superklasse betrachten .

Zum Beispiel werden wir eine Methode erstellen, die dies in einem Merkmal zurückgibt :

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

5. Schnittstellen

Ein Merkmal kann auch Schnittstellen implementieren, genau wie reguläre Klassen.

Lassen Sie uns eine Schnittstelle erstellen und in einem Merkmal implementieren :

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

Implementieren wir nun die abstrakte Methode der Schnittstelle in der Implementierungsklasse:

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

6. Eigenschaften

Wir können einem Merkmal Eigenschaften hinzufügen, wie wir es in jeder regulären Klasse tun würden:

trait UserTrait implements Human { String email String address }

7. Eigenschaften erweitern

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

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:

trait AddressTrait { String residentialAddress } trait EmailTrait { String email } trait Person implements AddressTrait, EmailTrait {}

8. Multiple Inheritance Conflicts

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.

8.1. Default Conflict Resolution

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

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.

8.2. Explicit Conflict Resolution

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

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:

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

9. Implementing Traits at Runtime

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

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

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

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

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

10. Conclusion

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

Ein Merkmal ist ein wirklich effektiver Weg, um gemeinsame Implementierungen und Funktionen in unseren Klassen hinzuzufügen. Darüber hinaus können wir redundanten Code minimieren und die Codewartung vereinfachen.

Wie üblich sind die Code-Implementierungen und Unit-Tests für diesen Artikel auf GitHub verfügbar.