2009-09-18 2 views
136

この問題のコードの簡略化されたバージョンは、1つの汎用クラスはジェネリック型パラメータを持つ別のクラスを使用し、可変引数パラメータでメソッドをジェネリック型のいずれかを通過する必要がある:"varargsパラメータ用に作成されたTの一般的な配列"コンパイラ警告を解決することは可能ですか?

class Assembler<X, Y> { 
    void assemble(X container, Y... args) { ... } 
} 

class Component<T> { 
    void useAssembler(T something) { 

     Assembler<String, T> assembler = new Assembler<String, T>(); 

     //generates warning: 
     // Type safety : A generic array of T is 
     // created for a varargs parameter 
     assembler.assemble("hello", something); 
    } 

}

この警告に遭遇することなく、genericパラメータをvarargsメソッドに渡す正しい方法はありますか?あなたは一般的な配列を作成することはできませんので、

assembler.assemble("hello", new T[] { something }); 

よう

もちろん何かが動作しません。

+3

奇妙なもの。コンパイラがここで完全な型の安全性を保証できるように思われる。 – erickson

+1

私の考えはちょうど –

+0

Angelika LangerのJavaジェネリックスFAQの関連エントリー:http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#Why%20does%20the%20compiler%20sometimes%20issue%20an%20unchecked%20warning% 20when%20I%20invoke%20a – Flow

答えて

78

@SuppressWarnings("unchecked")を追加する以外は、そうは思わない。

このbug reportには、より多くの情報がありますが、一般的な型の配列が好きではないコンパイラにまでわかります。

+3

私は@SuppressWarnings( "未チェック")を避けたいと思いました。そのバグレポートは私にはほとんど希望がありません! –

+3

ジョシュア・ブロッホが「有効なJava」に「ジェネリックと配列を混用しないでください」と書いています。 – Timmos

+15

その後、暗黙のうちに、GenericsでVarargsを使用しないでください!そうです...コレクションではなくArrayにvarargをマップするという決定は、永遠にJavaを刺すようになります。ゴスリング氏にうまくやった。 – bernstein

1

解決するのは非常に簡単な問題です。List<T>を使用してください!

参照型の配列は使用しないでください。

Java(1.7)の現在のバージョンでは、メソッドを@SafeVargsとマークすると、呼び出し元から警告が削除されます。しかし、注意しておけば、レガシーアレイを使用しない方が良いでしょう。

テックノートImproved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methodsも参照してください。

+1

なぜ避けなければなりませんか? – idrosid

+6

これはvarargsパラメータでやむを得ないでしょうか? –

+1

参照の配列は、従来のものであり、ジェネリックでうまくいきません(たとえば、誤ったサブタイプの規則があるなど)ため、避けるべきです。 'List'はより便利ですが、arrayは未処理の実装の詳細です。 –

0

ジェネリック型の配列を扱う場合、ジェネリック型への参照を強制的に渡します。これで、java.lang.reflect.Arrayを使って実際に汎用コードを実行できます。あなたがメソッドをオーバーロードしていることができ

http://java.sun.com/javase/6/docs/api/java/lang/reflect/Array.html

+0

私はジェネリック型の配列ではなく、直接的ではなく、汎用型のvarargsで作業しています。 –

1

。 これはあなたの問題を解決していませんが、それは警告の数を最小限に抑えることが

class Assembler<X, Y> { 
    void assemble(X container, Y a1) { ... } 
    void assemble(X container, Y a1, Y a2) { ... } 
    void assemble(X container, Y a1, Y a2, Y a3) { ... } 
    void assemble(X container, Y a1, Y a2, Y a3, Y a4) { ... } 
    void assemble(X container, Y... args) { ... } 
} 
+22

Ew。これはまさにvarargsが防ぐことになっている種類のハックです。 –

+1

これは有効なアプローチです。たとえば、Guavaの[ImmutableSet.of](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableSet)をご覧ください。 html#of(E))。 – Jonathan

55

トムホーティンがコメントでこれを指摘したが、より明示的なことを(はい、それはハックです!):はい、あなたが解決することができますこれは宣言サイト(潜在的に多くの呼び出しサイトではなく)で行います。JDK7に切り替えます。

あなたがJoseph Darcy's blog postで見ることができるように、プロジェクトコイン運動は、Java 7のためのいくつかの小さな増分言語の改善を選択するには、この警告は、それがあることが知られていた状況で離れて行く作るための方法側で@SuppressWarnings("varargs")ような何かをできるようにするBob Lee's proposalを受け入れ安全。

これはOpenJDKでthis commitで実装されています。

これはあなたのプロジェクトにとって有益かもしれません(多くの人がJFSのリリース前の不安定版に切り替えるのは喜ばしくないでしょう)が、おそらくそれは後でこの質問を見つけたJDK7がリリースされた後に)有用だと思います。

+7

上記のProject Coin機能が使用可能になりました。Java 7の[@SafeVarargs](http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html)を参照してください。 –

+1

AndroidあなたはハニーJava 7 ??? !!! –

+0

ボブの提案の代替Eが魅力的です。 –

5

パラメータをvarargメソッドの呼び出しで明示的にオブジェクトにキャストすると、コンパイラは@SuppressWarningsに頼らずに幸せになります。

public static <T> List<T> list(final T... items) 
{ 
    return Arrays.asList(items); 
} 

// This will produce a warning. 
list("1", 2, new BigDecimal("3.5")) 

// This will not produce a warning. 
list((Object) "1", (Object) 2, (Object) new BigDecimal("3.5")) 

// This will not produce a warning either. Casting just the first parameter to 
// Object appears to be sufficient. 
list((Object) "1", 2, new BigDecimal("3.5")) 

ここでの問題は、コンパイラがどのような具体的なタイプの配列を作成する必要があるかということです。メソッドが汎用でない場合、コンパイラはメソッドの型情報を使用できます。メソッドが汎用の場合、呼び出し時に使用されるパラメータに基づいて配列型を把握しようとします。パラメータタイプが同種のものであれば、その作業は簡単です。それらが異なる場合、コンパイラは私の意見ではあまりにも巧妙であることを試み、ユニオンタイプの汎用配列を作成します。それから、それについてあなたに警告するように強く感じます。より簡単に型を絞り込むことができない場合、Object []を作成するのが簡単な解決策でした。上記の解決策はそれだけを強要します。

これをよりよく理解するには、以下のlist2メソッドと比較して、上記のリストメソッドに対する呼び出しを行います。

public static List<Object> list2(final Object... items) 
{ 
    return Arrays.asList(items); 
} 
+0

これは、例えば次のようにも機能します。 if(if、hasNext()){class = it.next()。getClass(); }たとえば、オブジェクトのクラスを不明な型の配列から取得するために使用します。 – ggb667

15

流暢なタイプのインターフェースの後であれば、ビルダーパターンを試してみることができます。 varargsのように簡潔ではありませんが、タイプセーフです。

静的な汎用型のメソッドは、型の安全性を保持しながら、ビルダーを使用するときに定型文の一部を削除できます。

それを

import static com.example.ArgBuilder.*; 

public class VarargsTest { 

    public static void main(String[] args) { 
     doSomething(new ArgBuilder<String>().and("foo").and("bar").and("baz")); 
     // or 
     doSomething(with("foo").and("bar").and("baz")); 
    } 

    static void doSomething(Iterable<String> args) { 
     for (String arg : args) { 
      System.out.println(arg); 
     } 
    } 
} 
+0

合成力です。私はこれがvarargsよりもはるかに好きです、それはもっと表現力があります。 –

1

を使用したビルダー

public class ArgBuilder<T> implements Iterable<T> { 

    private final List<T> args = new ArrayList<T>(); 

    public ArgBuilder<T> and(T arg) { 
     args.add(arg); 
     return this; 
    } 

    @Override 
    public Iterator<T> iterator() { 
     return args.iterator(); 
    } 

    public static <T> ArgBuilder<T> with(T firstArgument) { 
     return new ArgBuilder<T>().and(firstArgument); 
    } 
} 

は、Java 7以降の方法に@SafeVarargsを追加したり、クライアントコードに注釈を付ける必要はありません。

class Assembler<X, Y> { 

    @SafeVarargs 
    final void assemble(X container, Y... args) { 
     //has to be final... 
    } 
} 
関連する問題