23

このような変更できない基本クラスがある場合:

public abstract class A {
    public abstract Object get(int i);
}

そして、私は次のようなクラスでそれを拡張しようとしBます:

public class B extends A{
    @Override
    public String get(int i){
        //impl
        return "SomeString";
    }
}

全て大丈夫。しかし、次のようにすると、より一般的なものにしようとして失敗します。

public class C extends A{
    @Override
    public <T extends Object> T get(int i){
        //impl
        return (T)someObj;
    }
}

これを禁止する理由が思いつきません。私の理解では、ジェネリック型Tは にバインドされており、Objectこれは の要求された戻り値の型ですA。戻り値の型としてStringorを の中に入れることができるのに、クラスの中に入れることができないのはなぜですか?AnyObjectB<T extends Object> TC

私の観点からすると、別の奇妙な動作は、次のような追加のメソッドです。

public class D extends A{

    @Override
    public Object get(int i){
        //impl
    }

    public <T extends Object> T get(int i){
        //impl
    }
}

も許可されていませんDuplicateMethod。少なくともこれは私を混乱させます.Javaは決定を下すべきだと思います.同じ戻り値の型である場合は、オーバーライドを許可しないでください. そうでない場合、このメソッドを追加できないのはなぜですか? 常識に基づいて、同じであるがオーバーライドできないと言うのは非常に奇妙です。

4

8 に答える 8

11

JLS # 8.4.2。メソッド署名

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

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

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

上記のルールに従って、親には消去がなく、子供には消去があるため、有効なオーバーライドではありません。

JLS#8.4.8.3。オーバーライドと非表示の要件

例 8.4.8.3-4。消去はオーバーライドに影響します

クラスは、同じ名前とタイプの消去を持つ 2 つのメンバー メソッドを持つことはできません。

class C<T> {
    T id (T x) {...}
}
class D extends C<String> {
    Object id(Object x) {...}
}

D.id(Object) は D のメンバーであり、C.id(String) は D のスーパータイプで宣言されているため、これは不正です。

  • 2 つのメソッドは同じ名前、id を持っています。
  • C.id(String) は D からアクセス可能です
  • D.id(Object) の署名は、C.id(String) の署名のサブ署名ではありません
  • 2 つの方法の消去方法は同じです。

クラスの 2 つの異なるメソッドは、同じ消去のメソッドをオーバーライドできません。

 class C<T> {
     T id(T x) {...}
 }
 interface I<T> {
     T id(T x);
 }
 class D extends C<String> implements I<Integer> {
    public String  id(String x)  {...}
    public Integer id(Integer x) {...}
 }

D.id(String) は D のメンバーであり、D.id(Integer) は D で宣言されているため、これも不正です。

  • 2 つのメソッドは同じ名前、id を持っています。
  • D.id(Integer) は D からアクセス可能です
  • 2 つのメソッドには異なるシグネチャがあります (どちらも他方のサブシグネチャではありません)。
  • D.id(String) は C.id(String) をオーバーライドし、D.id(Integer) は I.id(Integer) をオーバーライドしますが、オーバーライドされた 2 つのメソッドの消去は同じです

また、スーパーから子に許可される場合の例を示します

サブシグネチャの概念は、シグネチャが同一ではないが、一方が他方をオーバーライドできる 2 つのメソッド間の関係を表すように設計されています。具体的には、シグネチャがジェネリック型を使用しないメソッドが、そのメソッドのジェネリック バージョンをオーバーライドできるようにします。これは、ライブラリの設計者が、ライブラリのサブクラスまたはサブインターフェースを定義するクライアントとは無関係にメソッドを自由に生成できるようにするために重要です。

例を考えてみましょう:

class CollectionConverter {
List toList(Collection c) {...}
}
class Overrider extends CollectionConverter {
 List toList(Collection c) {...}

}

ここで、このコードがジェネリックの導入前に書かれたものであると仮定し、クラス CollectionConverter の作成者がコードを生成することを決定したとします。

 class CollectionConverter {
   <T> List<T> toList(Collection<T> c) {...}
 }

特別な措置がなければ、Overrider.toList は CollectionConverter.toList をオーバーライドしなくなります。代わりに、コードは違法になります。ライブラリの作成者は既存のコードを移行することを躊躇するため、これはジェネリックの使用を著しく阻害します。

于 2012-09-25T08:49:21.330 に答える
5

最初の部分の答えは、たとえ消去が同じであっても、Java では非ジェネリック メソッドをジェネリック メソッドでオーバーライドすることはできないということです。これは、オーバーライド メソッドを次のようにしても機能しないことを意味します。

 public <T extends Object> Object get(int i)

Java がこの制限を課す理由はわかりません (少し考えてみました)。ジェネリック型をサブクラス化するために実装された特殊なケースに関係していると思います。

2 番目の定義は、基本的に次のように変換されます。

public class D extends A{

    @Override
    public Object get(int i){
    //impl
    }

    public Object get(int i){
        //impl
    }

}

これは明らかに問題です。

于 2012-09-25T09:51:46.763 に答える
2

JLS セクション8.4.8.3 オーバーライドと非表示から:

型宣言 T にメンバー メソッド m1 があり、T または T のスーパータイプで宣言されたメソッド m2 が存在し、次のすべての条件が満たされる場合、コンパイル時エラーになります。

  1. m1 と m2 は同じ名前です。

  2. m2 は T からアクセスできます。

  3. m1 の署名は、m2 の署名のサブ署名 (§8.4.2) ではありません。

  4. m1 または一部のメソッド m1 オーバーライドの署名 (直接的または間接的) には、m2 または一部のメソッド m2 オーバーライドの署名 (直接的または間接的) と同じ消去があります。

1と2のホールド。

3 も成立します ( JLS セクション 8.4.2からの引用):

サブシグネチャの概念は、シグネチャが同一ではないが、一方が他方をオーバーライドできる 2 つのメソッド間の関係を表すように設計されています。具体的には、シグネチャがジェネリック型を使用しないメソッドが、そのメソッドのジェネリック バージョンをオーバーライドできるようにします。

そして、あなたは別の方法を持っています:ジェネリックのないものをオーバーライドするジェネリック型のメソッド。

消去された署名が同じであるため、4 も成立します。public Object get(int i)

于 2012-09-25T09:57:00.123 に答える
1

次の呼び出しを想像してみてください。

A a = new C();
a.get(0);

実際には、ジェネリックメソッドを呼び出していますが、型引数を渡していません。現状では、これはそれほど問題ではありません。これらの型引数は、とにかくコード生成中に消えます。それでも、具体化がテーブルから外されることはなく、Javaのスチュワードは、言語がその扉を開いたままにしようと試み続けています。型引数が具体化された場合、呼び出しはそれを必要とするメソッドに何も提供しません。

于 2012-09-25T08:57:59.247 に答える
1

@RafaelT の元のコードでは、このコンパイル エラーが発生します...


C.java:3: error: C is not abstract and does not override abstract method get(int) in A
public class C extends A { 
       ^
C.java:5: error: name clash: <T>get(int) in C and get(int) in A have the same erasure, yet neither overrides the other
    public  <T extends Object> T  get ( int i ){ 
                              ^
  where T is a type-variable:
    T extends Object declared in method <T>get(int)

@RafaelT の C サブクラスを正常にコンパイルするための最も単純で簡単な解決策は...


public abstract class A {
    public abstract Object get(int i);
}

public class C<T> extends A {
    @Override
    public T get(int i){
        //impl
        return (T)someObj;
    }
}

私は他の人々が彼らの答えで善意を持っていたと確信しています. それにもかかわらず、いくつかの回答は JLSを大きく誤解しているようです。

上記のクラス宣言のみの変更により、コンパイルが成功します。その事実だけでも、@ RafaelT の元の署名はサブ署名として完全に問題がないことを意味します—他の人が示唆したこととは反対です.

私は、回答した他の人が犯したと思われるのと同じ間違いを犯すつもりはなく、JLSジェネリックのドキュメントを完全に理解しているふりをしようとします。私は、OP の最初のコンパイルの失敗の根本原因を 100% 確実に把握できていないことを告白します。

しかし、Javaのジェネリックの典型的な紛らわしくて風変わりな微妙さに加えて、OPが戻り値の型として使用するという不幸な組み合わせと関係があると思います。Object

于 2016-10-22T04:03:24.113 に答える
0

あなたは消去について読むべきです。

あなたの例では:

public class D extends A{

  @Override
  public Object get(int i){
      //impl
  }

  public <T extends Object> T get(int i){
      //impl
  }
}

ジェネリックメソッド用にコンパイラが生成したバイトコードは、非ジェネリックメソッドと同じになります。つまりT、上限に置き換えられます。つまり、Objectです。DuplicateMethodこれが、IDEで警告が表示される理由です。

于 2012-09-25T09:01:16.593 に答える
0

method を as として宣言しても、メソッドの引数または外側のクラスのフィールドなど、他の場所<T extends Object> T get(int i)で宣言しないと意味がありません。T後者の場合、クラス全体を type でパラメータ化するだけTです。

于 2012-09-25T09:59:02.713 に答える
0

概念上の問題があります。C#get()オーバーライドを仮定しますA#get()

A a = new C();
Object obj = a.get();  // actually calling C#get()

C#get()必要ですT- 何をすべきTですか?判別する方法はありません。

T消去により不要になったことを抗議することができます。それは今日正しいです。ただし、消去は「一時的な」回避策と見なされていました。ほとんどの型システムは消去を想定していません。実際には、Java の将来のバージョンで既存のコードを壊すことなく完全に「具体化可能」、つまり消去せずに作成できるように慎重に設計されています。

于 2012-09-25T16:32:33.900 に答える