19

クラスに 2 つのコンストラクターがあるとします。

public User (List<Source1> source){
...
}

public User (List<Source2> source) {
...
}

これらのコンストラクターはどちらも User に関する同じ情報を提供し、さまざまなユースケースでユーザーを構築する有効な方法であるとしましょう。

Java では、型消去のためにこれを行うことはできません。Java は、パラメーターとして List< ? を持つ 2 つのコンストラクターを受け入れません。>。

それで、これを回避する方法は何ですか?やり過ぎではなく、基本的な OO を尊重するソリューションは何ですか? Java が強力なジェネリックをサポートしていないという理由だけで、ファクトリ メソッドやその他のインターフェイスを構築する必要があるのは間違っているようです。

私が考えることができる可能性は次のとおりです。

1)List<?>コンストラクターのパラメーターとして a を受け入れ、必要なロジックの種類をコンストラクターで解析するか、受け入れられた型のいずれでもない場合は例外をスローします。

2) いずれかのリストを受け取り、適切なユーザー オブジェクトを構築して返すクラスを作成します。

3) ラッパーを作成List<Source1>List<Source2>、代わりに User コンストラクターに渡すことができます。

4) この男を 2 つのクラスでサブクラス化します。コンストラクターを除くすべての機能が継承されます。一方のコンストラクターは Source1 を受け入れ、もう一方は Source2 を受け入れます。

5) インスタンス化のための 2 つの異なるデータ ソース用の 2 つの異なるビルダー メソッドがあるビルダーでこの男をラップします。

私の質問は次のとおりです。

1) これを行う必要があるのは Java の欠陥ですか、それとも意図的な設計上の決定ですか? 直感とは何ですか?

2) 不要な複雑さを導入することなく、適切なコードを維持するという点で最も強力なソリューションはどれですか? なんで?

この質問は似ています: Javaでの型消去に関するコンストラクターの設計ですが、詳細には触れず、さまざまな回避策を提案するだけです。

4

5 に答える 5

10

通常のアプローチは、ファクトリ メソッドを使用することです。

public static User createFromSource1(List<Source1> source) {
    User user = new User();
    // build your User object knowing you have Source1 data
    return user;
}

public static User createFromSource2(List<Source2> source) {
    User user = new User();
    // build your User object knowing you have Source2 data
    return user;
}

orを使用した構築のみが必要な場合(つまり、デフォルトのコンストラクターがない場合) は、コンストラクターを非表示にして、クライアントにファクトリ メソッドを強制的に使用させます。Source1Source2

private User () {
    // Hide the constructor
}

この問題は、コンストラクターに別の名前を付けることができないために発生します。これは、これらが通常のメソッドである場合にこれを克服する方法です。コンストラクタ名はクラス名に固定されているため、このコード パターンは区別して同じ型を消去する唯一の方法です。

于 2012-06-07T19:03:33.690 に答える
1

1: 消去との下位互換性を維持します。

2: あなたのクラスはジェネリックを使用できますか? このようなもの:

public class User<T> {
    private List<T> whatever;
    public User(List<T> source){
       ....
    }
}

これがあなたの意図したものかどうかはわかりません (2)

于 2012-06-07T18:48:54.667 に答える
1

基本的な問題は、ジェネリックが存在する前に言語が設計された (コンストラクター名が固定されている)ため、通常はメソッドの名前を変更してそれらを区別することで処理される型消去による衝突を処理できないことです。

ファクトリ メソッドに頼らない 1 つの「回避策」は、別の型指定されていないパラメーターを追加して、コンパイラーがそれらを区別できるようにすることです。

public User(List<Source1>, Source1 instance) {
    // ignore instance
}

public User(List<Source2>, Source2 instance) {
    // ignore instance
}

ただし、これらの余分なパラメーターを何かに置き換えることができ (たとえばInteger、 and String、または単にそれらの 1 つに 2 番目のパラメーターを省略させる)、それでも機能するため、これは少し不自由です。さらに、余分なパラメーターは無視されます - コンストラクターを区別するためだけに存在します。それでも、余分なメソッドや特別なコードを追加しなくても、コードは機能します。

于 2012-06-08T01:55:24.717 に答える
0
public class User<T> {

    public User(List<T> source){

    }

}

またはおそらくより良い:

public class User<T extends SomeTypeOfYours> {

    public User(List<T> source){

    }
}

wheerはandSomeTypeOfYoursのスーパータイプです。Source1Source2

于 2012-06-07T18:54:15.350 に答える
0

私は一般的にファクトリーのアイデア、またはユーザーのジェネリック化(@Markus Mikkolainenによって提案されているように)が好きですが、可能な代替手段の1つは、リストのクラスを2番目の引数として渡し、それをオンにすることです。

public User<List<?> source, Class<?> clazz) {
   switch(clazz) {
      case Source1.class: initForSource1(); break;
      case Source2.class: initForSource2(); break;
      default: throw new IllegalArgumentException();
   }
}

共通の<?>祖先クラスがある場合は、別のものになる可能性があります。これが悪い考えである多くのケースを想像することができますが、それが許容できる場合はいくつかあります.

于 2012-06-07T19:20:45.403 に答える