2

それぞれが同じフィールドと同じメソッドを持ついくつかの列挙型があります。

public enum AddressSubType {
    DOM("dom"), INTL("intl"), POSTAL("postal");

    private final String keyword;
    private AddressSubType(String keyword) {
        this.keyword = keyword;
    }
    public String getKeyword() {
        return keyword;
    }
    @Override
    public String toString() {
        return keyword;
    }
}

public enum EmailSubType {
    INTERNET("internet"), X400("x.400");

    private final String keyword;
    private EmailSubType(String keyword) {
        this.keyword = keyword;
    }
    public String getKeyword() {
        return keyword;
    }
    @Override
    public String toString() {
        return keyword;
    }
}

これらの列挙型がフィールドとメソッドを (親クラスのように) 共有する方法はありますか? enums を拡張できないことはわかっています。ありがとう。

4

5 に答える 5

1

私がそれを言う人になります。これはひどい考えです。

定数の固定セットを表す必要があるときはいつでも列挙型を使用する必要があります。これには、太陽系の惑星や、コンパイル時にすべての可能な値がわかっているデータセット(メニューの選択、コマンドラインフラグなど)などの自然な列挙型が含まれます。 ソース

列挙型は、内部にハードコードされた値を除いて、他には何も気にしません。通常、オブジェクト指向の方法で物事をグループ化することを決定するとき、それらはすべてのオブジェクトが関連していることを確認します。列挙型であるため、これらのファイルは、Objectのサブタイプである2つのクラスよりも関連性がありません。ドメイン内のすべての列挙型間で機能を共有したい場合は、いくつかの静的関数、またはよく参照されるユーティリティクラスを確認する必要があります(これには、1日の終わりに独自の一連の問題があります)。基本的に、クラスにはすべての共有ロジックをカプセル化する一連の関数があり、署名は通常次のようになります。

function foo(Enum enumeration)
于 2012-06-26T00:24:37.973 に答える
1

この場合、できることはあまりありません。より複雑な例であっても、共通コードを配置するのに最適な場所は、すべての列挙型が使用できるユーティリティ クラスか、列挙型に含まれる別のクラスです。コンポジション経由 (各列挙型には、おそらく Keyword と呼ばれるそのクラスのインスタンスがあります)。

メソッドのコードtoStringが複雑で、各列挙でそれを再記述したり、含まれているオブジェクトに移動したりしたくない場合、Java 8 には使用できるメカニズムがあります。この例ではやり過ぎです。列挙型がすべて使用するインターフェイスを定義できます。インターフェイスは状態を持つことができないため、状態 (キーワード) は引き続き列挙型に存在する必要がありますが、Java 8 以降では、メソッドのデフォルトの実装を提供できます。

public interface Common {
    String getKeyword();

    String toString() default {
        return getKeyword();
    }

    String toDescriptiveString() default {
        char firstLetter = getKeyword().charAt(0);
        boolean vowel =
            firstLetter == 'a' || firstLetter == 'e' ||
            firstLetter == 'i' || firstLetter == 'o' ||
            firstLetter == 'u' || firstLetter == 'x';
        // the letter x is pronounced with an initial vowel sound (eks)
        return (vowel?"an ":"a ") + getKeyword() + " address";
    }
}

あなたの列挙型はこのインターフェースを実装します:

public enum AddressSubType implements Common {
    DOM("dom"), INTL("intl"), POSTAL("postal");

    private final String keyword;

    private AddressSubType(String keyword) {
        this.keyword = keyword;
    }

    @Override
    public String getKeyword() {
        return keyword;
    }

    @Override
    public String toString() {
        return Common.super.toString();
    }
}

public enum EmailSubType implements Common {
    INTERNET("internet"), X400("x.400");

    private final String keyword;

    private EmailSubType(String keyword) {
        this.keyword = keyword;
    }

    @Override
    public String getKeyword() {
        return keyword;
    }

    @Override
    public String toString() {
        return Common.super.toString();
    }
}

toStringメソッドの奇妙な新しい構文に注目してください。インターフェースのデフォルト メソッドの規則は、メソッド解決が常にインターフェースよりもクラス メソッドを優先することです。したがって、toStringinのデフォルトの実装を提供しても、enum の実装Commonが優先され、enum に実装Objectがない場合は in の実装が優先されます。Objectしたがって、 ( toString、 またはhashCode、 または など)のメソッドの 1 つに取って代わるインターフェイスの既定のメソッドを使用する場合は、equalsこの新しいinterface.super.method()構文で明示的に呼び出す必要があります。

toDescriptiveStringただし、メソッドのために余分なフープをジャンプする必要はありません。これは の新しいものでinterface Commonあり、enum によって提供されないため、インターフェースによって提供されるデフォルトの実装を取得します。(独自のメソッドでオーバーライドしたい場合は、他の継承されたメソッドと同じようにできます。)

もちろん、オブジェクトの他のメソッドと同様に、デフォルトのメソッドを使用できます。

public class Test {
    public static void main(String[] args) {
        for (AddressSubType a : AddressSubType.values()) {
            System.out.println(a.toDescriptiveString());
        }
        for (EmailSubType e : EmailSubType.values()) {
            System.out.println(e.toDescriptiveString());
        }
    }
}

どちらが出力されますか:

dom アドレス
国際アドレス
住所
インターネットアドレス
x.400 アドレス

ただし、この場合、かなり冗長なtoDescriptiveStringメソッドがなければ、enum クラスは、ない場合よりも少し短くなることはありませんinterface Common。インターフェースのデフォルト メソッドは、既存のインターフェースに新しい機能を追加する場合に真価を発揮します。これは、以前のバージョンの Java のインターフェースのすべての実装者を壊さない限り不可能なことです。

これらはすべて、 Lambda を使用したまだ不完全な Java SE 8 に基づいています。プレリリース ビルドをダウンロードできますが、これは進行中の作業であることに注意してください。

于 2012-06-26T00:57:07.473 に答える
1

interface両方が実装できる を宣言できます。これにより、そのインターフェイスの特定のメソッドのみを処理するメソッドに、いずれかの列挙型を引数として渡すことができます。ただし、これにより、フィールドやメソッドの実装ではなく、メソッド シグネチャのみを「共有」できます。

列挙型が与えられた例のように些細な場合、コードの繰り返しはそれほど多くないため、おそらく問題にはなりません。メソッドのコードがより複雑で反復的であることがわかった場合は、その責任を別のクラスに委任することを検討する必要があります。

EmailAddress継承パターン (例: "is a" )を本当にモデル化したい場合は、Addressから離れる必要がありますenums。いくつかの静的フィールドを使用して列挙型パターンをシミュレートできますが、それぞれを特定のクラスのインスタンスにすることができます。

于 2012-06-25T23:59:25.333 に答える
1

私はおそらくそれらを 1 つの列挙型オブジェクトに結合し、「Postal」フラグを true に設定して初期化するものと、「email」フラグを true に設定するものがあります。これは、この 2 つはアドレスの「タイプ」が異なるだけだからです。

その後、個別にアクセスしたい場合はイテレータを返すか、全体を反復することができます。

また、コードの残りの部分が単純化されていることに気付くかもしれません。たとえば、「住所」のコレクションを持ち、特定の住所が電子メールか郵便かを実行時にチェックするだけです。

しかし、それは実際にどれだけ似ているかによって異なります。

于 2012-06-26T00:01:14.237 に答える
1

Value クラスを作成できます

public class Value {

  private final String keyword;

  private Value(String keyword) {
    this.keyword = keyword;
  }
  public String getKeyword() {
    return keyword;
  }
  @Override
  public String toString() {
    return keyword;
  }
}

次に、次のような public static final 値を持つクラスを作成できます。

public class AddressSubType extend Value {

  public static final AddressSubType DOM = new AddressSubType("DOM");
  public static final AddressSubType INTL = new AddressSubType("intl");
  ...

  private AddressSubType(String keyword) {
     super(keyword);
  }
}
于 2012-06-26T00:04:45.740 に答える