web-dev-qa-db-ja.com

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

defaultメソッドをJava 8の特性の貧乏人バージョン)として使用するのは安全な習慣ですか?

クールだからといってそれらを使うとpandas sad になるかもしれないと主張する人もいますが、それは私の意図ではありません。デフォルトのメソッドは、APIの進化と後方互換性をサポートするために導入されましたが、これは間違いではありませんが、それ自体を特性として使用することを間違えたりねじれたりすることはありません。

次の実用的な使用例 を念頭に置いています:

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

または、おそらくPeriodTraitを定義します:

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

確かに、合成(またはヘルパークラス)を使用することもできますが、より冗長で雑然としており、多態性の恩恵を受けられません。

だから、基本的な特性としてデフォルトのメソッドを使用しても大丈夫/安全ですか、または予期しない副作用を心配する必要がありますか?

いくつかの質問 on SOは、Java vs Scala traits; that's not not point)ここでは、単に意見を求めるのではなく、信頼できる答えまたは少なくともフィールドの洞察を探しています。企業プロジェクトの特性としてデフォルトの方法を使用した場合、それは時限爆弾であることが判明しました?

115
youri

簡単な答えは次のとおりです。安全に使用すれば安全です:)

卑劣な答え:yo特性によって何を意味するか教えてください、そしておそらく私はあなたにもっと良い答えを与えます:)

すべての深刻さにおいて、「特性」という用語は明確に定義されていません。多くのJava開発者は、Scalaで表現される特性に最も精通していますが、Scalaは、名前または効果のいずれかで特性を持つ最初の言語とはほど遠いです。

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

繰り返しますが、Scalaでは、線形化によって特性が構成されます。クラスAが特性XYを拡張する場合、XYが混在する順序により、XYは解決されました。 Javaでは、この線形化メカニズムは存在しません(一部は、「Javaに似ていない」ため拒否されました)。

インターフェイスにデフォルトのメソッドを追加する主な理由は、インターフェイスの進化をサポートすることでしたが、それを超えることを十分に認識していました。あなたがそれを「インターフェースの進化++」または「特性」とみなすかどうかは、個人的な解釈の問題です。したがって、安全性に関する質問に答えるには、メカニズムが実際にサポートするものに固執している限り、サポートされていないものに望みどおりに伸ばそうとするのではなく、大丈夫です。

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

設計目標の範囲内にあるいくつかの使用例を次に示します。

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

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

  • 便利な方法。これらは利便性のためだけのメソッドであり、一般にクラスのデフォルト以外のメソッドに関しても実装されています。最初の例のlogger()メソッドは、これを合理的に説明したものです。

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

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

118
Brian Goetz