Groovyの特性の紹介

  • link:/category/programming/ [プログラミング]

  • Groovy

1. 概要

このチュートリアルでは、https://www.baeldung.com/groovy-language [Groovy]で特性の概念を調べます。 これらはGroovy 2.3リリースで導入されました。

2. 特性とは何ですか?

トレイトは、複数のクラスの機能を拡張するために使用できる一連のメソッドまたは動作を表す再利用可能なコンポーネントです。
このため、*デフォルトの実装と状態の両方を保持する「インターフェイス」と見なされます。 すべての特性は、_trait_キーワードを使用して定義されます。*

3. 方法

_trait_でメソッドを宣言することは、クラスで通常のメソッドを宣言することに似ています。 ただし、_trait_でprotectedまたはpackage-privateメソッドを宣言することはできません。
パブリックメソッドとプライベートメソッドの実装方法を見てみましょう。

3.1. パブリックメソッド

最初に、_public_メソッドが_trait._でどのように実装されるかを探ります。
_UserTrait_という名前の_trait_と_public_ _sayHello_メソッドを作成してみましょう。
trait UserTrait {
    String sayHello() {
        return "Hello!"
    }
}
その後、_UserTrait_を実装する_Employee_クラスを作成します。
class Employee implements 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. プライベートメソッド

_trait_で_private_メソッドを作成し、別の_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: []"
}
_trait、_では、プライベートメソッドは、他のパブリックメソッドでは必要ですが、クラスでオーバーライドされるべきではない実装には不可欠です。

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!!"
    }
}
  • Traitsは_protected_および_private_スコープをサポートしていません。*

4.  _this_キーワード

the__ this__キーワードの動作は、Javaの動作と似ています。 _trait_を_super_クラスと見なすことができます。
たとえば、_trait_で_this_を返すメソッドを作成します。
trait UserTrait {
    def self() {
        return this
    }
}

5. インターフェース

_trait_は、通常のクラスが行うように、インターフェイスも実装できます。
_interface_を作成して、_trait_に実装しましょう:
interface Human {
    String lastName()
}
trait UserTrait implements Human {
    String showLastName() {
        return "Hello, ${lastName()}!"
    }
}
それでは、実装クラスで_interface_の_abstract_メソッドを実装しましょう。
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. 複数の継承の競合

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

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

*デフォルトでは、_implements_句で最後に宣言された_trait_のメソッドが選択されます*。
したがって、特性は、http://www.lambdafaq.org/what-about-the-diamond-problem/ [Diamond Problem]に遭遇することなく、複数の継承を実装するのに役立ちます。
最初に、同じシグネチャを持つメソッドを使用して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_参照を使用して呼び出すメソッドを明示的に選択することで、それをオーバーライドできます。
たとえば、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. 実行時の特性の実装

_trait_を動的に実装するために、* as_キーワードを使用して、実行時にオブジェクトを_trait_に強制することができます*。
たとえば、_basicBehavior_メソッドで_AnimalTrait_を作成しましょう:
trait AnimalTrait {
    String basicBehavior() {
        return "Animalistic!!"
    }
}
複数の特性を一度に実装するには、_as_キーワードの代わりに_withTraits_メソッドを使用できます。
def dog = new Dog()
def dogWithTrait = dog.withTraits SpeakingTrait, WalkingTrait, AnimalTrait

10. 結論

この記事では、Groovyで特性を作成する方法を見て、それらの便利な機能のいくつかを調べました。
_trait_は、クラス全体に共通の実装と機能を追加するための非常に効果的な方法です。 さらに、冗長なコードを最小限に抑え、コードのメンテナンスを容易にします。
いつものように、この記事のコード実装と単体テストはhttps://github.com/eugenp/tutorials/tree/master/core-groovy[over on GitHub]で入手できます。