突然ですが、Traitってどんなものかはみなさんご存知でしょうか?
私はこれまで長くソフトウェアエンジニアとして働いてきていますが、大変恥ずかしながらTraitという考え方にこれまで出会ったことがなく、全く知りませんでした💦
(たまたまこれまで使ってきた言語にtraitがなかったからという言い訳)
最近Kotlinを書き始める中で、実際にTraitを使う必要に迫られたので、自分なりにTraitについて説明をしてみたいと思います。
Traitってなに?
端的に言うと、Abstractやクラスの継承は縦方向の共通化であるのに対し、Traitとは横方向に処理を共通化するための方法です。
複数のクラスで共通して使われる処理を記述する方法として、継承はJavaの初期からある考え方ですが、traitはその後追加された考え方で、php、Ruby, Rust, Scala,Pythonでも導入されています。
参考:https://en.wikipedia.org/wiki/Trait_(computer_programming)
abstractの理解がある方であれば、abstractの様に具体的なメソッドが記述できるinterfaceだと考えてもらえると理解しやすいかもしれません。
Abstract, Interface, Traitの違いと特徴
abstract
まず、abstractは下図の通り、抽象メソッド・具象メソッドを保つことができ、それを利用するクラスはabstractで定義された”クラス”を”継承(extends)"します。一つのクラスは同時に一つのクラスだけを継承することができます。
抽象メソッドはメソッド名とその戻り値のみを定義したメソッド、具象メソッドはその中の処理も定義したものを指します。
interface
次にinterfaceです。interfaceは抽象メソッドのみ持つことができます。
このinterfaceを利用するクラスはこれを"実装(implement)"します。実装は継承と異なり、複数のinterfaceを同時に実装することができます。
Trait
最後にtraitです。traitの場合、interfaceではできなかったinterfaceに具象メソッドを持つことができます。また、利用クラスはinterfaceと同様にtraitを"実装"するため、複数のtraitを同時に持つことが可能となります。
表でまとめる
以上のことを表にまとめてみると以下の通りとなります。
※ただ、これは基本的な考え方であり、言語によって可能なことが異なることに注意してください
abstract | interface | trait | |
---|---|---|---|
実装(一つ以上) | できない | できる | できる |
継承(一つだけ) | できる | できない | できない |
具象メソッドの定義 | できる | できない | できる |
抽象メソッドの定義 | できる | できる | できる |
プロパティの定義 | できる | できない | できない |
Typescript, Java, Kotlin言語での実装
各言語のtraitの扱いについて調べてみました。いずれもtraitという構文はないものの、traitの考え方に合った実装を実現することはできそうでした。
https://kotlinlang.org/docs/interfaces.html#properties-in-interfaces
Typescript
Typescriptでは、クラスをimplementすることができ、これをミックスインと呼んでいます。
Java
Javaでは、Java8からinterfaceにもdefaultメソッドやstaticメソッドを書ける様になっています。これを使うことでtraitの様な処理を書くことができます
Kotlin
Kotlinでは、interfaceに具象メソッドを記述することができます。そのため、interfaceはtraitの特徴も包含していると考えることができます。
※ 元々Kotlinではtraitは存在した様ですが、2015年にリリースされたM12からtraitはdepricatedになり、interfaceに統合された様です
https://blog.jetbrains.com/kotlin/2015/05/kotlin-m12-is-out/
まとめ
traitをうまく利用することにより、各クラスに散らばっている処理を1箇所にまとめることができます。同じ様な処理が複数のクラスに点在してしまっている場合は、その処理をtraitに移動することができないかを考えてみましょう。