私は最近、この異常な(私にとっては)Java構文に出くわしました...以下に例を示します。
List list = new <String, Long>ArrayList();
<String, Long>
型の引数の位置に注意してください...通常の型の後ではなく、前です。この構文を見たことがないことを認めても構いません。 ArrayList
が1のみの場合、2つの型引数があることに注意してください。
型引数の配置は、型の後に配置するのと同じ意味を持ちますか?そうでない場合、異なるポジショニングは何を意味しますか?
ArrayList
の値が1のみの場合に、2つの型引数を使用することが正当なのはなぜですか?
例えば、いつもの場所を検索しました。アンジェリカランガーとここでは、ANTLRプロジェクトのJava文法ファイルの文法規則以外に、この構文についての言及はどこにもありません。
これは異常ではありませんが、完全に有効なJavaです。理解するには、クラスにジェネリックコンストラクターがある可能性があることを知る必要があります。次に例を示します。
_public class TypeWithGenericConstructor {
public <T> TypeWithGenericConstructor(T arg) {
// TODO Auto-generated constructor stub
}
}
_
ジェネリックコンストラクターを介してクラスをインスタンス化する場合、型引数を明示的にする必要はほとんどないでしょう。例えば:
_ new TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));
_
T
は明らかにLocalDate
です。ただし、Javaが型引数を推測(推定)できない場合があります。次に、質問の構文を使用して明示的に指定します。
_ new <LocalDate>TypeWithGenericConstructor(null);
_
もちろん、読みやすさや何らかの理由で必要だと思われる場合は必要ではありませんが、提供することもできます。
_ new <LocalDate>TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));
_
あなたの質問では、_Java.util.ArrayList
_コンストラクターを呼び出しているようです。そのコンストラクタはジェネリックではありません(ArrayList
クラスのみが全体として、それは別のものです)。 Javaが使用されていないときに呼び出しで型引数を提供できる理由については、以下の私の編集を参照してください。私のEclipseは私に警告を与えます:
ArrayList型の非ジェネリックコンストラクターArrayList()の未使用の型引数。引数でパラメータ化しないでください
しかし、それはエラーではなく、プログラムは正常に実行されます(List
およびArrayList
の型引数が欠落しているという警告も表示されますが、これはまた別の話です)。
型引数の配置は、型の後に配置するのと同じ意味を持ちますか?そうでない場合、異なるポジショニングは何を意味しますか?
いいえ、違います。通常の型引数/ 後型(ArrayList<Integer>()
)は汎用クラス用です。型引数beforeは、constructor用です。
2つの形式を組み合わせることもできます。
_ List<Integer> list = new <String, Long>ArrayList<Integer>();
_
リストにInteger
オブジェクトが格納されていることがわかるので、これはもう少し正しいと考えます(もちろん、意味のない_<String, Long>
_は省いたほうがいいでしょう)。
ArrayListが1つしかないのに、2つの型引数を持つのはなぜ合法なのですか?
まず、型の前に型引数を指定する場合は、クラスではなくコンストラクタに正しい数を指定する必要があります。したがって、ArrayList
クラスが持っている型引数の数とは何の関係もありません。つまり、この場合、コンストラクターは型引数を受け取らないため(汎用ではないため)、何も指定しないでください。とにかく何かを提供する場合、それらは無視されます。そのため、あなたが提供する数や数は重要ではありません。
リンクの@Slawに感謝して編集します。Javaは、すべてのメソッド呼び出しで型引数を許可します。呼び出されたメソッドがジェネリックである場合、型引数が使用されます。そうでない場合、それらは無視されます。例えば:
_ int length = "My string".<List>length();
_
はい、それはばかげています。 Java言語仕様(JLS)は、サブセクション15.12.2.1でこの正当性を示しています。
この規則は、互換性の問題と代替可能性の原則に由来します。インターフェースまたはスーパークラスはサブタイプとは無関係に生成される可能性があるため、ジェネリックメソッドを非ジェネリックメソッドでオーバーライドできます。ただし、オーバーライド(非ジェネリック)メソッドは、明示的に型引数を渡す呼び出しなど、ジェネリックメソッドの呼び出しに適用できる必要があります。それ以外の場合、サブタイプはそのジェネレートされたスーパータイプの代わりになりません。
コンストラクターは直接オーバーライドできないため、この引数はコンストラクターには当てはまりません。しかし、すでに複雑なルールを複雑にしすぎないように、同じルールを使用したかったのではないでしょうか。いずれにしても、インスタンス化とnew
に関するセクション15.9.3は、15.12.2を参照しています。
どうやら、非ジェネリックメソッド/コンストラクターの前に好きなジェネリックパラメーターを付けることができます。
new <Long>String();
Thread.currentThread().<Long>getName();
コンパイラーは、これらの型の引数を実際の汎用パラメーターと一致させる必要がないため、気にしません。
コンパイラが引数をチェックする必要があるとすぐに、不一致について不平を言います:
Collections.<String, Long>singleton("A"); // does not compile
私にはコンパイラのバグのようです。