2016-11-24 4 views
3

私の目標は、Javaコレクションに基づいて1対nのコンテナクラスを作成することです。 (ほぼMap<String,Collection<Double>>のようなものです - 単一の文字列の場合、マップはn個の倍精度を保持できます。たとえば、キー "coordinate"の場合、コンテナは値[3.1,4.3、7.2]を保持できます)。Javaジェネリックス:設定可能なバリューホルダータイプを使用してto-nコレクションを作成する方法

必要な特徴は、キーごとにn個の要素を保持する型を自由に定義できることです。したがって、上記の例のCollectionは、ListまたはSetで置き換えられる必要があります。

次のことが可能でなければならず、それがある場合には、目標に到達する:

package com.fun.with.generics; 

import java.lang.reflect.Constructor; 
import java.util.Collection; 
import java.util.HashMap; 

/** 
* A map that maps a single key to n values. 
* 
* @param <K> The key type. 
* @param <V> The value type. 
* @param <V> The type used as container for the to-n elements. 
*/ 
@SuppressWarnings("serial") 
public class OneToN2<K,V,CT extends Collection<V> > extends HashMap<K, Collection<V>> 
{ 
    private final Constructor<CT> _containerCtor; 

    /** 
    * Create an instance. 
    */ 
    public OneToN2(Class<CT> claß) 
    { 
     try 
     { 
      _containerCtor = claß.getConstructor(); 
     } 
     catch (Exception e) 
     { 
      throw new IllegalArgumentException("Need default ctor."); 
     } 
    } 

    /** 
    * Add a single key value pair. 
    * 
    * @param key The key. 
    * @param element The element to add. 
    */ 
    synchronized public void add(K key, V element) 
    { 
     Collection<V> ct = get(key); 

     if (ct == null) 
     { 
      ct = makeToN(); 
      put(key, ct); 
     } 

     ct.add(element); 
    } 

    /** 
    * Add an element for multiple keys. 
    * 
    * @param keys The keys to add. 
    * @param element The element the keys refer to. 
    */ 
    synchronized public void add(Collection<K> keys, V element) 
    { 
     for (K c : keys) 
      add(c, element); 
    } 

    /** 
    * Get a newly allocated container holding the n values. 
    */ 
    @Override 
    public Collection<V> get(Object key) 
    { 
     Collection<V> n = 
       super.get(key); 
     Collection<V> result = 
       makeToN(); 

     result.addAll(n); 

     return result; 
    } 

    /** 
    * Create a new instance of the to-n container. 
    */ 
    private Collection<V> makeToN() 
    { 
     try 
     { 
      return _containerCtor.newInstance(); 
     } 
     catch (Exception e) 
     { 
      throw new InternalError("Could not create to-n collection."); 
     } 
    } 
} 

注私は唯一の最小セットを保持:

OneToN2<String, Double, List<Double>> cl = 
    new OneToN2<>(ArrayList.class); 
OneToN2<String, Double, Set<Double>> cs = 
    new OneToN2<>(HashSet.class); 

は今、私は次のコードで開始私の意図を示す手術の

これまでのところ、私は技術的に自分の意図を表現することができ、コンパイラはそれを受け入れたので、最初のステップで成功しました。

は今問題に:私は上記の型のインスタンスを作成することはできませんよ:)

次の例のユースケースは、使用目的を示すが、それらのいずれも取り組んでいる。

意図したソリューション

// Declaration (this compiles): 
OneToN2<String, Double, List<Double>> x; 
// Initialisation (does not compile). Error is Type mismatch: cannot 
// convert from OneToN2<String,Double,ArrayList> 
// to OneToN2<String,Double,List<Double>> 
x = new One2N2<>(ArrayList.class); 

Bummerですが、コンパイラはArrayList.classがListであると推測できないことを理解しています。

// Declaration (this compiles): 
OneToN2<String, Double, ArrayList<Double>> x; 
// Initialisation (does not compile). Error is Type mismatch: cannot 
// convert from OneToN2<String,Double,ArrayList> 
// to OneToN2<String,Double,ArrayList<Double>> 
x = new One2N2<>(ArrayList.class); 

が再び多かれ少なかれ一定のエラーメッセージが表示されて、残念:だから私はあまり理想的ではなく許容可能な代替手段を試してみました。

問題はどこですか?

(なぜ私はそれを望んでいるのですか?学問的興味、ジェネリックスの楽しさですが、正直なところ、いくつかのシステムで同様のタイプを作成したため、to-nコンテナが固定されているのでコードが重複しています。

+0

あなたはグアバmultimapのを使用して考えられていますか? –

答えて

2

をあなたは、Java 8を使用している場合は、 Supplierは物事をきれいにするでしょう。

public class OneToN2<K,V, CT extends Collection<V> > extends HashMap<K, Collection<V>> 
{ 
    private final Supplier<CT> _containerCtor; 

    /** 
    * Create an instance. 
    */ 
    public OneToN2(Supplier<CT> _containerCtor) 
    { 
     this._containerCtor = _containerCtor; 
    } 

...

/** 
    * Create a new instance of the to-n container. 
    */ 
    private Collection<V> makeToN() 
    { 
     return _containerCtor.get(); 
    } 
} 

次に、あなたがそうのようなあなたのコレクションをインスタンス化することができます

public static void main(String[] args) { 
    OneToN2<String, Double, List<Double>> x = new OneToN2<>(ArrayList::new); 
} 
+0

ほとんど同じ文字列の同じ答えです: –

+0

私は見ました:)。あなたはそれを削除しましたか?私はあなたの方が良いと思った。追加するものがあれば自由に編集してください。 – teppic

2

CTタイプパラメータを削除します。コンストラクタにClassを提供する代わりに、内部コレクションの作成に使用するファクトリを提供します(インターフェイス経由、またはJava 8ではメソッドのみを提供できます)。

public interface CollectionFactory<V> { 
    Collection<V> createCollection(); 

public class OneToN2<K,V> implements Map<K, Collection<V>> { 
    public OneToN2(CollectionFactory<V> factory) { ... } 
    ... 
} 

はまた、私はあなたが(すなわち、相続以上の組成を好む)それをサブクラス化HashMapの代わりを使うお勧めします。

+0

継承を超えて合成します。 'HashMap > - > HashMap 'で使用することができ、 'Collection 'の代わりに 'CT'を返すようにすることができます共分散のために問題ありません)。 –

+0

@AndyTurner(クラス化されたクラスの 'class'オブジェクトのトラブルを避けるために)コンストラクタでコレクションクラスを提供するのではなく、ファクトリを使用するという解決策ですか?私は 'CT'の削除は、オピニオン/コンテキストベースであると思います(コレクションのタイプが意味的にあなたの文脈でタイプ情報の一部であるかどうかによって決まります)。 –

+0

継承上の合成。私はHashMap実装が提供する実装が私のニーズに合っているので、概念を完全に理解し、意図的に継承を決定しました。しかしこれは副次的なポイントです。 –

0

まあ、これは私のIDEで罰金コンパイル:

OneToN2<String, Double, List<Double>> x = new OneToN2(ArrayList.class); 

new OneToN2

<>を処分した。また、このリンクが有用であることが分かるかもしれません:Passing a class with type parameter as type parameter for generic method in Java

+1

これは、警告が出ると思います:未確認の割り当て。しかし、宣言された型は正しく型付きされているので、動作しますが、<>の除去には不安です。なぜそれが最終的に違いが生じないのであれば、なぜそれらを使うのですか? (私は本当に私に質問しています。私は答えが不思議です) –

+0

良いことは、このトリックがコンパイルエラーを解決することです。しかし、代わりに警告を紹介します。実際に<>を削除すると違いが生じます。 <>は、コンパイラが定義から型を推論するようにコンパイラに要求し、そうするとき型が互換性がないと結論づけます。 Genericsよりも前の既存のコードをサポートするために導入されたコンパイラ互換モードに<> outスイッチを多かれ少なかなど置いてください。 –

関連する問題