特性としてのJava 8のデフォルトメソッド:安全?


116

デフォルトのメソッドを Java 8の貧困層の特性のトレイトとして使用することは安全な方法ですか?

パンダをそれだけで使うと悲しいかもしれないと主張する人もいます。クールだからですが、それは私の意図ではありません。また、APIの進化と下位互換性をサポートするためにデフォルトのメソッドが導入されたこともよく思い出されますが、これは、それ自体を特性として使用することを間違ったり、歪ませたりするものではありません。

私が持っている以下の実用的なユースケースを念頭に置きました:

public interface Loggable {
    default Logger logger() {
        return LoggerFactory.getLogger(this.getClass());
    }
}

または、次のように定義しますPeriodTrait

public interface PeriodeTrait {
    Date getStartDate();
    Date getEndDate();
    default isValid(Date atDate) {
        ...
    }
}

確かに、コンポジションを使用することもできます(ヘルパークラスを使用することもできます)が、より冗長で雑然としており、ポリモーフィズムの恩恵を受けることができません。

では、デフォルトのメソッドを基本的な特性として使用しても問題ありませんか?それとも、予期しない副作用について心配する必要がありますか?

SOに関するいくつかの質問は、JavaとScalaの特性に関連しています。それはここでのポイントではありません。私も単に意見を求めるのではありません。代わりに、私は信頼できる回答または少なくともフィールドインサイトを探しています。企業プロジェクトの特性としてデフォルトの方法を使用した場合、それは時限爆弾であることが判明しましたか?


抽象クラスを継承することで同じ利点を得ることができ、パンダを泣かせることを心配する必要がないように思われます...インターフェイスでデフォルトのメソッドを使用するために私が見ることができる唯一の理由は、機能が必要であり、インターフェイスに基づくレガシーコードの束を変更します。
Deven Phillips、2015

1
独自の静的ロガーフィールドを定義する抽象クラスの拡張について、@ infosec812に同意します。logger()メソッドは、呼び出されるたびに新しいロガーインスタンスをインスタンス化しませんか?
Eidan Spiegel

ロギングについては、Projectlombok.orgとその@ Slf4jアノテーションを確認する必要がある場合があります。
Deven Phillips、2015

回答:


120

簡単に言えば、安全に使用すれば安全です:)

卑劣な答え:あなたが特性によって何意味するか教えてください、そして多分私はあなたにより良い答えを与えるでしょう:)

すべての真面目に、「特性」という用語は明確に定義されていません。多くのJava開発者は、Scalaで表現される特性に最も慣れていますが、Scalaは、特性または名前のいずれかを備えた最初の言語からはほど遠いです。

たとえば、Scalaでは、トレイトはステートフルです(var変数を持つことができます)。要塞ではそれらは純粋な振る舞いです。デフォルトのメソッドを持つJavaのインターフェースはステートレスです。これは彼らが特性ではないことを意味しますか?(ヒント:それはトリックの質問でした。)

繰り返しますが、Scalaでは、特性は線形化によって構成されます。クラスは、IF A特性を拡張するXY、その後に注文XYの間の競合方法を決定するで混合さXとがY解決されます。Javaでは、この線形化メカニズムは存在しません(「Javaに似ていない」ため、一部拒否されました)。

デフォルトのメソッドをインターフェースに追加するおおよその理由は、インターフェースの進化をサポートすることでしたが、私たちはそれを超えていることをよく知っていました。それを「インターフェイスの進化++」と見なすか「特性」と見なすかは、個人的な解釈の問題です。したがって、安全性についての質問に答えるには...メカニズムが実際にサポートしているものに固執する限り、サポートしていないものに伸ばそうとするのではなく、問題はありません。

主要な設計目標は、インターフェースのクライアントの観点からは、デフォルトのメソッドは「通常の」インターフェースメソッドと区別できないようにすることです。したがって、メソッドのデフォルト性は、インターフェースの設計者実装にとってのみ興味深いものです。

以下は、設計目標の範囲内にあるいくつかの使用例です。

  • インターフェイスの進化。ここでは、既存のインターフェースに新しいメソッドを追加します。既存のインターフェースには、そのインターフェースの既存のメソッドに関して適切なデフォルトの実装があります。例としては、forEachメソッドをCollectionに追加する場合があり、デフォルトの実装はiterator()メソッドの観点から記述されています。

  • 「オプション」メソッド。ここで、インターフェイスの設計者は、「実装者が必要とする機能の制限に対処する意思がある場合、このメソッドを実装する必要はない」と述べています。たとえば、Iterator.removeをスローするデフォルトが指定されましたUnsupportedOperationException。の実装の大部分はIteratorとにかくこの動作をするため、デフォルトではこのメソッドは本質的にオプションになっています。(の動作AbstractCollectionがのデフォルトとして表現されている場合Collection、変異メソッドに対しても同じことを行う可能性があります。)

  • 便利なメソッド。これらは便宜上のものであり、クラスのデフォルト以外のメソッドに関して一般的に実装されているメソッドです。logger()最初の例のメソッドは、これを合理的に示しています。

  • コンビネーター。これらは、現在のインスタンスに基づいてインターフェースの新しいインスタンスをインスタンス化する構成メソッドです。たとえば、メソッドPredicate.and()またはComparator.thenComparing()はコンビネータの例です。

デフォルトの実装を提供する場合は、デフォルトの仕様も指定する必要があります(JDKでは、@implSpecこのためにjavadocタグを使用します)。実装者がメソッドをオーバーライドするかどうかを理解するのに役立ちます。コンビニエンスメソッドやコンビネーターなどの一部のデフォルトは、ほとんどオーバーライドされません。オプションのメソッドのような他のものはしばしばオーバーライドされます。デフォルトが何を約束するかについて、十分な仕様(ドキュメントだけでなく)を提供する必要があります。これにより、実装者は、それをオーバーライドする必要があるかどうかについて賢明な決定を下すことができます。


9
ブライアン、この包括的な回答をありがとう。今、私はデフォルトのメソッドを軽いハートで使うことができます。読者:インターフェイスの進化とデフォルトのメソッドに関するBrian Goetzの詳細情報は、たとえばNightHacking Worldwide Lambdasにあります。
youri

@ brian-goetzに感謝します。あなたが言ったことから、Javaのデフォルトのメソッドは、Ducasseらの論文(scg.unibe.ch/archive/papers/Duca06bTOPLASTraits.pdf)で定義されている従来の特性の概念に近いと思います。Scalaの「特性」は、状態を持ち、その構成に線形化を使用しているため、私にはまったく特性とは思えません。また、メソッドの競合も暗黙的に解決しているように見えます。これらはすべて、従来の特性にはないものです。持ってる。実際、Scalaの特性は、特性というよりミックスインのようなものです。どう思いますか?PS:私はScalaでコーディングしたことがありません。
adino 2017年

リスナーとして機能するインターフェースのメソッドに空のデフォルト実装を使用するのはどうですか?リスナーの実装は、いくつかのインターフェースメソッドをリッスンすることにのみ関心がある場合があるため、メソッドをデフォルトにすることで、実装者はリッスンする必要のあるメソッドを実装するだけで済みます。
Lahiru Chandima 2017

1
@LahiruChandima好きMouseListenerですか?このAPIスタイルが理にかなっている限り、これは「オプションのメソッド」バケットに適合します。オプション性を明確に文書化してください!
Brian Goetz 2017

はい。のようにMouseListener。ご返信ありがとうございます。
Lahiru Chandima 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.