7

そこで、以前の質問の 1 つに答えるメソッドを作成しようとしていましたそのために、私は JLS を読んでいました。

次のクラスがあるとします。

public class A<T> {
    public void foo(T param){};
}

public class B extends A<String> {
    public void foo(String param){};
}

B.fooこの場合、オーバーライドすることは明らかですがA.foo、このケースが仕様にどのように適合するかはわかりません。

メソッドのオーバーライドに関して、JLS §8.4.8.1は次のように述べています。

クラス C で宣言されたインスタンス メソッド m1 は、次のすべてが当てはまる場合、クラス A で宣言された別のインスタンス メソッド m2 をオーバーライドします。

  1. C は A のサブクラスです。

  2. m1 の署名は、m2 の署名のサブ署名 (§8.4.2) です。

  3. また:

    • m2 が public、protected、または C と同じパッケージ内でデフォルト アクセスで宣言されている、または
    • m1 は、m3 が m2 をオーバーライドするように、メソッド m3 (m1 とは異なる m3、m2 とは異なる m3) をオーバーライドします。

明らかに、ポイント 1 と 3 はこのケースで満たされています。JLS でサブ署名の意味をもう少し詳しく見てみましょう。JLS §8.4.2は次のように述べています。

名前と引数の型が同じである場合、2 つのメソッドは同じシグネチャを持ちます。

次の条件がすべて満たされる場合、2 つのメソッドまたはコンストラクタ宣言 M と N は同じ引数の型を持ちます。

  1. それらは同じ数の仮パラメータを持っています (ゼロの可能性があります)

  2. それらは同じ数の型パラメーターを持っています (おそらくゼロ)

  3. A1, ..., An を M の型パラメーターとし、B1, ..., Bn を N の型パラメーターとする。N の型の Bi の各発生を Ai に名前変更した後、対応する型変数の境界は次のようになります。 M と N の仮パラメータの型は同じです。

私たちの場合、ポイント 1 は明らかに真です (両方とも 1 つの引数があります)。

ポイント 2 はもう少し曖昧です (ここで、仕様が正確に何を意味するのかわかりません)。どちらのメソッドも独自の型パラメーターを宣言しませんが、クラスをパラメーター化する型変数である which をA.foo使用します。T

だから私の最初の質問は: このコンテキストでは、クラスで宣言された Type 変数はカウントされますか?

では、それはカウントされないため、ポイント 2 は誤りであると仮定しましょうT(この場合、ポイント 3 を適用する方法がわかりません)。私たちの 2 つのメソッドは同じ署名を持っていませんが、それは のサブ署名であることを妨げませB.fooA.foo

JLS §8.4.2のもう少し先には、次のように書かれています。

次のいずれかの場合、メソッド m1 の署名は、メソッド m2 の署名のサブ署名です。

  1. m2 が m1 と同じ署名を持っている、または

  2. m1 の署名は、m2 の署名の消去 (§4.6) と同じです。

ポイント 1 が誤りであることは既に確認済みです。

JLS §4.6によるメソッドの消去署名は ですa signature consisting of the same name as s and the erasures of all the formal parameter types given in s。したがって、A.foo の消去は でfoo(Object)あり、B.foo の消去は ですfoo(String)。これら 2 つは異なる署名であるため、ポイント 2 も偽であり、B.foo は A.foo のサブ署名ではないため、B.foo は A.foo をオーバーライドしません。

それを除いて...

私は何が欠けていますか?私が見ていないパズルのピースがありますか、それともこの場合、仕様は本当に完全ではありませんか?

4

3 に答える 3

5

クラスで宣言された型変数はカウントされますか?

問題の要素はメソッドであり、含まれている型宣言ではありません。いいえ、それらはカウントされません。

では、T がカウントされないため、ポイント 2 が false であると仮定しましょう。

なんで?どちらも型パラメーターが 0 であるため、これは本当です。


ステップバイステップ:

  • それらは同じ数の仮パラメータを持っています (ゼロの可能性があります)

どちらのメソッドにも 1 つの仮パラメーターがあります。小切手。

  • それらは同じ数の型パラメーターを持っています (おそらくゼロ)

どちらのメソッドも型パラメーターを宣言しません。小切手。

A1, ..., An を M の型パラメータとし、B1, ..., Bn を N の型パラメータとする。N の型に出現する Bi の名前をそれぞれ Ai に変更すると、対応する型変数の境界は次のようになる。 M と N の仮パラメータの型は同じです。

どちらのメソッドも型パラメーターを宣言していないため、名前を変更する必要はなく、仮パラメーターの型は同じ --T[T=String] = Stringです。小切手。

B.foo(String)と同じ署名がありA<String>.foo(String)ます。⇒B.foo(String)の副署名ですA<String>.foo(String)

は publicBのサブクラスであるAため、overridesを結論付けることができます。A<String>.foo(String) B.foo(String)A<String>.foo(String)

于 2012-08-28T07:49:15.847 に答える
1

このように見なければなりません:

クラスは、メソッドを持つB型を拡張します。A<String>foo(String param)

A<String>ジェネリック型の呼び出しA<T>です。それはそれ自体のタイプです。これは、パラメーター化された型とは何かを定義するJLS 4.5によって暗示されています。A<String>パラメータ化された型であり、他の参照型と同等です。直接スーパークラスの概念について議論する場合、パラメーター化されているという事実は重要ではありません。

于 2012-08-28T07:46:31.153 に答える
0

ジェネリックに関するOracle のチュートリアルからの抜粋:

型消去後、メソッド シグネチャが一致しません。Node メソッドは setData(Object) になり、MyNode メソッドは setData(Integer) になります。したがって、MyNode の setData メソッドは Node の setData メソッドをオーバーライドしません。この問題を解決し、型の消去後にジェネリック型のポリモーフィズムを維持するために、Java コンパイラはブリッジ メソッドを生成して、サブタイピングが期待どおりに機能するようにします。

したがって、基本的に、コンパイラ マジックは、JLS をそのまま使用しても、コンパイルされたコードを期待どおりに動作させます。この動作がどこで指定されているかわかりません。

于 2012-08-28T07:49:21.477 に答える