1. ホーム
  2. java

[解決済み] コンストラクタの型引数は、型の前に置かれた場合、どのような意味を持つのでしょうか?

2022-06-28 11:29:54

質問

私は最近、(私にとって)珍しいJavaの構文に出会いました。

List list = new <String, Long>ArrayList();

の位置関係に注目してください。 <String, Long> 型の引数の位置に注目してください...通常の型の後ではなく、前です。私はこの構文を見たことがないことを認めます。また、2つの型引数があることに注意してください。 ArrayList には1つしかないのに、2つの型引数があることにも注意してください。

型の引数の位置は、型の後に置くのと同じ意味があるのでしょうか?そうでない場合、異なる位置づけは何を意味するのでしょうか?

のとき、なぜ2つの型引数を持つことが合法なのでしょうか? ArrayList は1つしかないのですか?

Angelika Langer やここなど、通常の場所を探しましたが、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()の未使用の型引数です。 引数でパラメータ化してはいけません。 Long>

しかし、これはエラーではなく、プログラムは正常に実行されます。 ListArrayList がありますが、これはまた別の話です)。

汎用クラスと汎用コンストラクタの比較

型引数の位置は、型引数の後に置くのと同じ意味ですか? と同じ意味を持つのでしょうか?そうでない場合、異なる位置づけは何を意味するのでしょうか? 位置づけは何を意味するのでしょうか?

いいえ、違います。通常の型引数/s の後に の後に、その型 ( ArrayList<Integer>() ) は 汎用クラス . 型引数 の前に コンストラクタ .

この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 Language Specification (JLS) は、サブセクション 15.12.2.1 でこの正当性を示しています。

<ブロッククオート

このルールは互換性の問題と置換可能性の原則に由来します。インターフェースやスーパークラスはサブタイプとは独立して生成されるので ジェネリックメソッドを非ジェネリックメソッドでオーバーライドすることができます。 を非ジェネリックメソッドでオーバーライドすることができます。ただし、オーバーライドする(非ジェネリック)メソッドは を含むジェネリックメソッドの呼び出しに適用可能でなければなりません。 型引数を明示的に渡す呼び出しを含む、ジェネリックメソッドの呼び出しに適用できなければなりません。そうでなければ、そのサブタイプは サブタイプはその生成されたスーパータイプに置き換えられない。

コンストラクタは直接オーバーライドできないので、この議論は成り立ちません。しかし、すでに複雑なルールをあまり複雑にしないために、同じルールにしたかったのでしょう。いずれにせよ、インスタンス化に関する15.9.3項と new は、15.12.2 を参照していることが多い。

リンク