「静的インターフェイス」は良い習慣ですか?


13

最近、インターフェイスに静的メソッドを持つオプションがあることに気付きました。インターフェイスの静的フィールドと同様に、興味深い動作があります。これらは継承されません。

実装される実際のインターフェースで有用かどうかはわかりません。ただし、プログラマは、ユーティリティクラスなどの静的なものの単なるエンベロープであるインターフェイスを作成できます。

簡単な例は、グローバル定数の単なるエンベロープです。クラスと比較して、public static final想定されているボイラープレートの欠落に簡単に気付くことができます(冗長性が低くなります)。

public interface Constants {
    String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
    int DEFAULT_TIMEOUT_MS = 30;
}

また、設定キーのこの疑似列挙のように、より複雑なものを作成することもできます。

public interface ConfigKeys {
    static createValues(ConfigKey<?>... values) {
        return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
    }

    static ConfigKey<T> key(Class<T> clazz) {
        return new ConfigKey<>(clazz);
    }

    class ConfigKey<T> {
        private final Class<T> type;
        private ConfigKey(Class<T> type) {
            this.type = type;
        }
        private Class<T> getType() {
            return type;
        }
    }
}

import static ConfigKeys.*;
public interface MyAppConfigKeys {
    ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
    ConfigKey<String> COMPANY_NAME = key(String.class);

    Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);

    static values() {
        return VALUES;
    }
}

この方法でユーティリティの「クラス」を作成することもできます。ただし、ユーティリティでは、プライベートヘルパーメソッドまたは保護されたヘルパーメソッドを使用すると便利な場合がよくありますが、これはクラスでは不可能です。

私はそれを素晴らしい新機能だと考えています。特に、静的メンバーが継承されないという事実は、インターフェースのみに導入された興味深い概念です。

あなたはそれを良い習慣とみなすことができるのだろうか。コードスタイルとベストプラクティスは公理的ではなく、意見の余地はありますが、意見を裏付ける正当な理由は通常あると思います。

この2つのようなパターンを使用する(しない)理由にもっと興味があります。


これらのインターフェイスを実装するつもりはないことに注意してください。それらは、静的コンテンツの単なるエンベロープです。定数またはメソッドのみを使用し、場合によっては静的インポートを使用するつもりです。


1
これに対する私のひざの反応は、それは悪いことだけにつながる可能性があるということです。パブリックスタティックメソッドは、本質的には名前空間付きグローバル関数にすぎませんが。インターフェースに実装コードを許可することは、実装が存在しないコントラクト定義としての目的に反するように感じます。部分的な実装は抽象クラスに任せます。これが追加された理由を読む必要があります。
エイドリアン

今のところ行かなければなりませんが、明日何かを書きます。簡単なことは、インターフェースがAbstractClassの目的から明確に分離された明確な目的を持つ前であるということです。インターフェースの言語にとらわれない定義に違反する方法で、それらは大きく重なります。さらに、デフォルトのメソッドを実装している場合、複数のインターフェースから継承できない場合があります。そのため、以前は存在しなかった複数のインターフェイスの継承には例外があります。Javaは、インターフェースの共通defintion ...から大幅に逸脱したので
エイドリアン

また、Interfaces、AbstractClasesなどの基本的なOOP概念を適切に処理しようとする新しい開発者、継承の代わりにコンポジションを使用する場合なども混乱させる可能性があります。
エイドリアン

Aliester、あなたはdefault方法について話している。私はstaticメソッドとフィールドについて話している。これらは継承されないため、多重継承を壊しません。
Vlasec

回答:


1

簡単な例は、グローバル定数の単なるエンベロープです。

Constant Interface Antipattern多くの場合、定数は単なる実装の詳細であるため、インターフェイスに定数を置くことは呼ばれます。さらなる欠点については、一定のインターフェースに関するウィキペディアの記事にまとめられています

この方法でユーティリティの「クラス」を作成することもできます。

実際、Java docには、たとえばデフォルトのメソッドからヘルパーメソッドを呼び出す場合に、静的メソッドを使用するとヘルパーメソッドを簡単に整理できることが記載されています。


1
この新しい機能がなくてもこれらの両方を行うことができるため、これは実際に提起された質問に対する答えではないと思います。
エイドリアン

ああ、ドキュメントでは、ヘルパーメソッドとして意図されています。ただし、公開されているため、矛盾しています。そのため、インターフェイスを実装するクラスを支援することも意図されている場合があります。ただし、これらは継承されないため、静的インポートを使用して、継承したように感じさせる必要があります。まあ、ドキュメントで調べてくれてありがとう。
Vlasec

2
これらの定数が何らかの形でインターフェイスで記述されたAPIの一部である場合、インターフェイスに定数を配置してもまったく問題はありません。アンチパターンである唯一のことは、メソッドを持たないインターフェイスに定数を入れ、クラスにクラスプレフィックスのない定数を参照するためだけにインターフェイスを実装させることです。これはインターフェースの乱用であり、静的インポートの導入以来、完全に廃止されました。
マイケルボルグワード

1

質問で述べたように、インターフェースは実装(またはインスタンス化)されることを意図していません。したがって、プライベートコンストラクターを持つクラスと比較できます。

  • クラスはno super()to callで拡張することはできませんが、インターフェイスは「実装」できます
  • クラスはリフレクションなしでインスタンス化することはできませんが、インターフェイスは次のような単純な匿名クラスとして簡単にインスタンス化できます。 new MyInterface() {};

この比較は、より多くの制御を可能にするため、クラスに有利です。そうは言っても、クラスは静的なものを保持するためのものではなく、インスタンスを持つことを期待するでしょう。まあ、完璧なオプションはありません。

「静的インターフェイス」からの継承についても奇妙なことがあります。

  • 「実装」クラスはフィールドを継承しますが、静的メソッドは継承しません
  • 拡張インターフェースは、静的メンバーをまったく継承しません
  • (クラスを拡張するクラスは、すべての非プライベートメンバーを継承しますが、インターフェイスによって拡張できないため、混乱の余地が少なくなります)

これらが悪いパターンであると考え、これらの場合に静的クラスを使い続ける理由になるかもしれません。ただし、シンタックスシュガーに常習している人にとっては、マイナス面でも魅力的かもしれません。


...とにかく、チームのコーディング規約はそれらの問題をカバーできます。私にとっては、プライベートコンストラクターを持つクラスほどプログラマーの手を縛るものではありませんが、セッターよりもリスクが少なく、セッターが頻繁に使用されます。
Vlasec 16

0

@MarkusPscheidtのように、この考え方は本質的に、Constant Interfaceアンチパターンの拡張です。このアプローチは当初、多くの異なるクラスに単一の定数セットを継承するために広く使用されていました。これがアンチパターンと見なされなくなった理由は、実際のプロジェクトでこのアプローチを使用して発生した問題のためでした。これらの問題は定数にのみ関連すると考える理由はありません。

おそらくもっと重要なことは、これを行う必要は本当にないということです。代わりに静的インポートを使用し、インポートするものとどこからインポートするかを明示してください。


はい、静的インポートは定数の継承よりも良いアイデアのように聞こえ、クラスとインターフェイスの両方の静的メンバーで動作します。また、クラス/インターフェース内でのみ表示されます。
Vlasec 16

@Vlasec正しいですが、インターフェイスでこれを行う必要はありません。クラスとインターフェイスでこれを行うことの唯一の違いは、複数のインターフェイスから継承できることです。このようなユーティリティクラスの標準的なプラクティスは、コンストラクターをプライベートにして、インスタンス化または拡張を防ぐことです。クラスよりもインターフェースでこれを行う利点はありません。誤用の扉を開くだけです。
ジミージェームズ16

インターフェイスの定数の値を確認できます。インターフェイスはメソッドを定義する傾向があり、メソッドは定数をパラメーターとして受け入れることがあります。例:interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }。私はそこにいるのを想像することができ、いくつかの異なる定数RGBRGBACMYKGREYSCALEINDEXEDなど
Stijn・デ・ウィット

@StijndeWittこれは最悪のものではありませんが、この目的のためにインターフェイスで列挙型またはネストされたクラスを使用できます。本当の問題は、これを使用して、継承を通じて多くのクラスのスコープに静的変数を取り込むことです。このアプローチは、静的インポートの使用に明らかに劣っています。
ジミージェームズ

0

新しい機能を適切に使用する方法に興味がある場合は、JDKのコードをご覧ください。インターフェイスのデフォルトメソッドは非常に広く使用されており、簡単に見つけることができますが、インターフェイスの静的メソッドは非常に一般的ではないようです。いくつかの例としてはjava.time.chrono.Chronologyjava.util.Comparatorjava.util.Map、とかなりの数のインターフェイスにjava.util.functionjava.util.stream

私が見た例のほとんどは、非常に一般的である可能性が高いAPIの使用を含んでいます。たとえば、時間APIの異なるタイプ間の変換。たとえば、関数に渡されるオブジェクトまたはコレクション。私の要点:APIに柔軟性が必要な部分があるが、ユースケースの80%を少数のデフォルトでカバーできる場合は、インターフェースで静的メソッドを使用することを検討してください。この機能がJDKで使用される頻度は低いと考えると、自分のコードでこの機能を使用するときは非常に保守的です。

あなたの例は、私がJDKで見ているようなものではありません。これらの特定のケースでは、ほぼ確実にenum目的のインターフェイスを実装するを作成する必要があります。インターフェースは一連の動作を記述し、実際にその実装のコレクションを維持するビジネスはありません。


-1

それを使用しない理由があるのだろうか。

古いJVMとの互換性はどうですか?

これは一般的に良い考えだと思いますか?

いいえ。これは、言語の表現力にzilchを追加する後方互換性のない変更です。2つの親指ダウン。

クラスを強く推奨するモデルの状況はありますか?

クラスを使用して実装の詳細を含めることを強くお勧めします。インターフェースは、実際には「このオブジェクトは操作XYZをサポートします」と言うことを意図したものであり、インターフェース宣言に入れると考えられる静的メソッドとは何の関係もありません。この機能は、ミニ冷蔵庫が組み込まれたリクライニングチェアのようなものです。確かにニッチな用途がいくつかありますが、主流になるに値するほどの重さはありません。

更新:これ以上ダウン票を送らないでください。明らかに、インターフェイスの静的メソッドがミツバチの膝であると考える人もいますが、私はここで降伏しています。この回答をすぐに削除しますが、それに添付されたコメントは興味深い議論です。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
maple_shaft

私があなたの質問に反対票を投じた場合、最初の2つの答えは本当の答えではないのでそれをします。言語の新しいバージョンは、物事を簡単にする新しいツールを提供します。それがこれらの新しいバージョンのまさに目的です。3番目の答えは、最初の2つの破壊された瓦で少し失われました。
Vlasec 16
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.