1. 概要

このチュートリアルでは、Groovyの特性の概念について説明します。 これらはGroovy2.3リリースで導入されました。

2. 特性とは何ですか?

トレイトは、複数のクラスの機能を拡張するために使用できる一連のメソッドまたは動作を表す再利用可能なコンポーネントです。

このために、 それらはインターフェースと見なされ、デフォルトの実装と状態の両方を保持します。 すべての特性は、traitキーワードを使用して定義されます。

3. メソッド

trait でメソッドを宣言することは、クラスで通常のメソッドを宣言することに似ています。 ただし、traitで保護されたメソッドまたはパッケージプライベートメソッドを宣言することはできません。

publicメソッドとprivateメソッドがどのように実装されているか見てみましょう。

3.1. パブリックメソッド

まず、方法を探ります公衆メソッドはで実装されます特性。

UserTraitという名前のtraitpublic sayHelloメソッドを作成しましょう。

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

その後、UserTraitを実装するEmployeeクラスを作成します。

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. プライベートメソッド

traitprivateメソッドを作成し、別のpublicメソッドで参照することもできます。

UserTrait:のコード実装を見てみましょう

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

実装クラスで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: []"
}

トレイトでは、プライベートメソッドは、他のパブリックメソッドでは必要ですが、どのクラスでもオーバーライドしてはならない実装に不可欠な場合があります。

3.3. 抽象メソッド

trait には、 abstract メソッドを含めることもできます。これらのメソッドは、別のクラスに実装できます。

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

3.4. デフォルトのメソッドのオーバーライド

通常、 trait には、パブリックメソッドのデフォルトの実装が含まれていますが、実装クラスでそれらをオーバーライドできます。

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

トレイトは、保護されたおよびプライベートスコープをサポートしていません。

4. thisキーワード

this キーワードの動作は、Javaの場合と同様です。 traitsuperクラスと見なすことができます。

たとえば、トレイトthisを返すメソッドを作成します。

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

5. インターフェース

trait も、通常のクラスと同じようにインターフェイスを実装できます。

インターフェースを作成し、それをトレイトに実装しましょう。

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

それでは、実装クラスにinterfaceabstractメソッドを実装しましょう。

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

6. プロパティ

通常のクラスの場合と同じように、トレイトにプロパティを追加できます。

trait UserTrait implements Human { 
    String email
    String address
}

7. 特性の拡張

通常のGroovyクラスと同様に、 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. 多重継承の競合

クラスが同じシグネチャを持つメソッドを持つ2つ以上の特性を実装する場合、競合を解決する方法を知る必要があります。 Groovyがデフォルトでこのような競合を解決する方法と、デフォルトの解決をオーバーライドする方法を見てみましょう。

8.1. デフォルトの競合解決

デフォルトでは、implements句で最後に宣言されたトレイトからのメソッドが取得されます

したがって、特性は、菱形継承問題に遭遇することなく多重継承を実装するのに役立ちます。

まず、同じシグネチャを持つメソッドを使用して2つのトレイトを作成しましょう。

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

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

次に、両方の特性を実装するクラスを作成しましょう。

class Dog implements WalkingTrait, SpeakingTrait {}

SpeakingTrait は最後に宣言されるため、そのbasicAbilityメソッドの実装はデフォルトでDogクラスで取得されます。

8.2. 明示的な競合の解決

これで、言語によって提供されるデフォルトの競合解決を単純に採用したくない場合は、trait.super.methodリファレンスを使用して呼び出すメソッドを明示的に選択することでをオーバーライドできます X210X]

たとえば、同じシグネチャを持つ別のメソッドを2つのトレイトに追加しましょう。

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. 実行時の特性の実装

トレイトを動的に実装するには、 asキーワードを使用して、実行時にオブジェクトをトレイトに強制変換します

たとえば、basicBehaviorメソッドを使用してAnimalTraitを作成しましょう。

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

一度に複数の特性を実装するには、asキーワードの代わりにwithTraitsメソッドを使用できます。

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

10. 結論

この記事では、Groovyで特性を作成する方法を確認し、それらの便利な機能のいくつかを調べました。

trait は、クラス全体に共通の実装と機能を追加するための非常に効果的な方法です。 さらに、冗長なコードを最小限に抑え、コードのメンテナンスを容易にします。

いつものように、この記事のコード実装と単体テストは、GitHubから入手できます。