2016-04-30 4 views
1

私は任意の数の配列をマージするメソッドを実装しようとしました。Javaでは配列のバリエーションが可能ですか?

@SuppressWarnings("unchecked") 
public static <T> T[] merge(T[]... arrs) { 
    int length = 0; 
    for (T[] arr : arrs) { 
     length += arr.length; 
    } 

    T[] merged = (T[]) new Object[length]; 
    int destPos = 0; 
    for (T[] arr : arrs) { 
     System.arraycopy(arr, 0, merged, destPos, arr.length); 
     destPos += arr.length; 
    } 
    return merged; 
} 

コンパイルしても問題ありませんでした。

String[] a = {"a", "b", "c"}; 
    String[] b = {"e", "f"}; 
    String[] c = {"g", "h", "i"}; 

    String[] m = merge(a,b,c); 

このコードは正常にコンパイルが、それは例外をスロー:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; 
    at ntalbs.test.ArrayMerge.main(ArrayMerge.java:25) 

コードがもっともらしいと正常にコンパイルに見えますが、動作しませんとで例外をスローそれから私はこのように、この方法をテストしましたランタイム。なぜこのコードが機能しないのか説明できますか?私は何が欠けていますか?

+5

私は問題は 'T []マージ=(T [])新しいオブジェクト[長さ]だと思う;'ありません配列varargs。 – torque203

+0

'String []'にキャストできない 'new Object []'を作成しています –

答えて

2

問題はvarargsのためではありません。

それが原因でラインのだ、

T[] merged = (T[]) new Object[length]; 

Object配列

String配列にキャストすることはできません。

使用、

T[] merged = (T[])java.lang.reflect.Array.newInstance(
          arrs[0].getClass().getComponentType(), length); 

注:@JBで指摘したように、あなたが同じ配列型をマージしようとした場合

、これはのみ動作します。異なる配列型をマージする場合は、すべての配列に共通のスーパー型を見つけて、arrs[0].getClass()の代わりに使用する必要があります。

+1

これは、すべての入力配列が同じ型である場合にのみ機能します。 String []とObject []をマージすると、オブジェクトを保持するString配列が作成されます。 –

+0

ありがとう@JBNizet ...それを指定するノートを入れてください。 – Codebender

+0

@ JBこれらは、マージメソッドのシグネチャと同じ型でなければなりません。 –

5

問題はvarargsとは関係ありません。

エラーメッセージには、Object[]が作成されて返され、それをString[]にキャストしています。しかし、Object[]String[]ではありません。したがって、例外。

コレクションのソースコード(とそのT[] toArray()メソッド)を見て、基本的に何をしようとしているのかを理解してください:T[]の配列を作成してください。

2

Tのクラスを使用して配列を作成できます。あなたがオブジェクトを渡したので、それらのオブジェクトから型を取得できます。このようなもの:

Class<T> clazz = arrs[0].getClass(); 
T[] arr = (T[]) Array.newInstance(clazz.getComponentType(), <length of all passed arrays>); 

次に、配列を入力してください。

+2

小さな修正ですが、newInstanceメソッドの 'clazz.getComponentType()'にする必要があります。さもなければ 'T'の配列型(' String'ではなく 'String []')を得るでしょう。 – Codebender

+0

申し訳ありませんが、チェックせずに書きました;)あなたは正しいです。 –

0

Object []をString []にキャストすることはできません。正しい実行時タイプのインスタンスを作成する必要があります。 そのためには、java.lang.reflect.Array#newInstanceメソッドを使用できます。 、適切な型を取得するには、次のようにマージメソッドにクラス引数を追加することができます。

public static <T> T[] merge(final Class<T> arrayType, T[]... arrs) { 
    int length = 0; 
    for (T[] arr : arrs) { 
     length += arr.length; 
    } 

    @SuppressWarnings("unchecked") 
    T[] merged = (T[]) Array.newInstance(arrayType, length); 
    int destPos = 0; 
    for (T[] arr : arrs) { 
     System.arraycopy(arr, 0, merged, destPos, arr.length); 
     destPos += arr.length; 
    } 
    return merged; 
} 

、これは実行時例外なしに実行することができます。

String[] a = { "a", "b", "c" }; 
    String[] b = { "e", "f" }; 
    String[] c = { "g", "h", "i" }; 

    String[] m = merge(String.class, a, b, c); 
0

はい作品を可変引数すべてのJavaタイプで使用できます。上記の例外は、T[] merged = (T[]) new Object[length];行によって生成され、の可変引数とは関係ありません。

配列は一般的に設計されていません。代わりにCollectionsフレームワークを使用することをお勧めします。あなたのコードの構造を変更できない場合

は、これに似た何かを試してみてください。

@SuppressWarnings("unchecked") 
public static <T> T[] merge(T[]... arrays) { 
    int fullSize = 0; 
    for (T[] array: arrays) { 
     fullSize += array.length; 
    } 
    List<T> concatenatedList = new ArrayList<T>(fullSize); 
    for (T[] array: arrays) { 
     concatenatedList.addAll(Arrays.asList(array)); 
    } 
    Class<T> targetType = (Class<T>)arrays 
     .getClass().getComponentType().getComponentType() 
    ; 
    return concatenatedList.toArray(
     (T[])Array.newInstance(targetType, concatenatedList.size()) 
    ); 
} 
関連する問題