Javaでの複数の汎用インターフェースの実装


10

特定の署名を含む特定のメソッドが利用可能であることを保証するインターフェイスが必要です。これまでのところ、彼は私が持っているものです:

public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

この問題は、クラスを他の複数のエンティティにマップできる場合に発生します。理想的なケースはこれです(javaではありません):

public class Something implements Mappable<A>, Mappable<B> {
    public A mapTo(A someObject) {...}
    public B mapTo(B someOtherObject) {...}
}

これを可能な限り「一般的な」ものにするための最良の方法は何でしょうか?

回答:


10

もちろん、これはType Erasureによってできることではありません。実行時には、2つのメソッドpublic Object mapTo(Object)があり、明らかに共存できません。

残念ながら、あなたがやろうとしていることは、Javaの型システムを超えているだけです。

ジェネリック型が常に最初のクラス型であり、それ自体がジェネリックではないと想定すると、メソッドmapTo(Object, Class)を使用することにより、同様の外向きの動作を実現できます。これにより、特定のクラスのランタイム検査を実行し、使用する動作を決定できます。明らかにこれはかなり洗練されておらず、戻り値を手動でキャストする必要がありますが、私はそれがあなたができる最善の方法だと思います。ジェネリック型自体がジェネリック型である場合、ジェネリックパラメーターも消去され、クラスが等しいため、このメソッドは機能しません。

ただし、@ Joachimの回答も指摘しておきます。これは、動作を個別のコンポーネントに分割して、問題全体を回避できる場合です。


3

ご覧のように、異なる型パラメーターを使用して同じインターフェイスを2回実装することはできません(消去のため、実行時にそれらは同じインターフェイスです)。

また、このアプローチは単一の責任の原則に違反します。クラスはSomething(意味が何であれ)であることを重視し、そのタスクへのマッピングAまたはそのタスクB に加えてマッピングを行うべきではありません。

あなたは本当にa Mapper<Something,A>とを持っているべきだと思いますMapper<Something,B>。このように、各クラスには明確に定義された単一の責任があり、同じインターフェイスを2回実装するという問題に遭遇することはありません。


まあ、アイデアは、クラスにその内容を他のオブジェクトに「変換」する責任を持たせることです。次に、クラスにとらわれない方法でそれらを処理するディスパッチャーがあり、したがってジェネリックス要件があります。ロジックの抽出について少し考えますが、実際にはクラスを2つに分割していることになりますが、それらは密結合されたままです(フィールドへのアクセスは許可する必要があります。
estani 2013年

@estani:はい、彼らは幾分密結合していますが、明確な責任があります。これについても考慮してCください。新しいクラスを導入し、それにSomethingマッピングできるようにしたい場合は、を修正する必要がありますがSomething、これは結合が多すぎます。新しいものを追加するだけで、SoemthingToCMapper煩わしさが軽減されます。
ヨアヒムザウアー2013年

1
これに+1-一般的に言えば、OPが望むもの(Javaで)を達成しようとする場合、継承よりも構成を優先する必要があります。デフォルトのメソッドを備えたJava 8はこれをさらに簡単にしますが、誰もがまだ最先端にいるわけではありません:-)。
Martijn Verburg 2013年

0

複数のインターフェースを実装することが許可されていない場合、カプセル化の使用を検討できます。(java8 +を使用した例)

// Mappable.java
public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

// TwoMappables.java
public interface TwoMappables {
    default Mappable<A> mapableA() {
         return new MappableA();
    }

    default Mappable<B> mapableB() {
         return new MappableB();
    }

    class MappableA implements Mappable<A> {}
    class MappableB implements Mappable<B> {}
}

// Something.java
public class Something implements TwoMappables {
    // ... business logic ...
    mapableA().mapTo(A);
    mapableB().mapTo(B);
}

詳細と例については、こちらをご覧ください。2つのジェネリック型を持つ1つのインターフェースを実装するJavaクラスを作成する方法は?


-1
public interface IMappable<S, T> {
    T MapFrom(S source);
}

// T - target
// S - source

UserをUserDTOにマップし、UserをUserViewModelにマップする場合は、2つの個別の実装が必要です。このロジックをすべて1つのクラスに積み上げないでください。それを行うのは意味がありません。

ヨアヒムを幸せに保つためのアップデート

public interface ITypeConverter<TSource, TDestination>
{
    TDestination Convert(TSource source);
}

しかし、今はAutomapperレルム(http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters)にいます。


IMappable他のものをマッピングするものには、良い名前ではないと思います。Mapper(またはIMapper、必要に応じて;-))の方がおそらく正しいでしょう。(ちなみに、いいえ、それは私の反対票ではありませんでした)。
Joachim Sauer 2013年

私は質問にあるものを取り上げ、それがインターフェースであることを強調するためにIを前に付けました。ネーミングの問題ではなく、質問に従ってデザインの問題を解決しています。
CodeART 2013年

1
申し訳ありませんが、私の意見では、設計の問題を実際に "解決"して名前を無視することはできません。デザインとは理解しやすい構造を意味します。間違ったネーミングは理解するのに問題です。
Joachim Sauer 2013年

更新はあなたの心を
静めるはずです

1
@CodeART私があなたの答えを正しく理解した場合、それは「MapFrom」(小文字;-)がオブジェクトを作成することを意味します。私の場合は、すでに作成されたオブジェクトに関する情報を入力するだけです。
estani 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.