6

静的メソッドの戻り型のジェネリックスは、継承とうまく調和していないようです。次のコードを見てください。

class ClassInfo<C>  {
  public ClassInfo(Class<C> clazz) {
    this(clazz,null);
  }  
  public ClassInfo(Class<C> clazz, ClassInfo<? super C> superClassInfo) {
  }
}

class A {
    public static ClassInfo<A> getClassInfo() {
        return new ClassInfo<A>(A.class);
    }
}

class B extends A {
    // Error: The return type is incompatible with A.getClassInfo()
    public static ClassInfo<B> getClassInfo() { 
        return new ClassInfo<B>(B.class, A.getClassInfo());
    }
}

A.getClassInfo()の戻りタイプを変更してこれを回避しようとしましたが、別の場所にエラーが表示されます。

class ClassInfo<C>  {
  public ClassInfo(Class<C> clazz) {
    this(clazz,null);
  }  
  public ClassInfo(Class<C> clazz, ClassInfo<? super C> superClassInfo) {
  }
}

class A {
    public static ClassInfo<? extends A> getClassInfo() {
        return new ClassInfo<A>(A.class);
    }
}

class B extends A {
    public static ClassInfo<? extends B> getClassInfo() { 
        // Error: The constructor ClassInfo<B>(Class<B>, ClassInfo<capture#1-of ? extends A>) is undefined
        return new ClassInfo<B>(B.class, A.getClassInfo());
    }
}

静的メソッドをこのように厳密にチェックする理由は何ですか?そして、どうすればうまくやっていくことができますか?メソッド名の変更は厄介なようです。

4

2 に答える 2

5

B の静的メソッドは、A の静的メソッドをオーバーライドしませんが、非表示にします。JLS 8.4.8.3は、戻り値の型が代入可能でなければならない、またはコンパイルされないことを明示的に示しています。

戻り値の型 R1 を持つメソッド宣言 d1 が、戻り値の型 R2 を持つ別のメソッド d2 の宣言をオーバーライドまたは非表示にする場合、d1 は d2 に対して戻り値の型を代入可能 (§8.4.5) である必要があります。そうしないと、コンパイル時エラーが発生します。

また、代入可能性は JLS #8.4.5 で定義されています。

次の条件が満たされる場合に限り、戻り値の型 R1 を持つメソッド宣言 d1 は、戻り値の型 R2 を持つ別のメソッド d2 の戻り値の型で代用可能です。

  • [...]
  • R1 が参照型の場合:
    • R1 が R2 のサブタイプであるか、R1 がチェックなしの変換 (§5.1.9) によって R2 のサブタイプに変換できるか、または
    • R1 = |R2|

あなたの場合: d1 は B のメソッド、R1 はClassInfo<B>、d2 は A のメソッド、R2 はClassInfo<A>です。AndClassInfo<B>は のサブタイプではありませんClassInfo<A>

ただし、ClassInfo<? extends B>に変換できますClassInfo<? extends A>。その動作は次の場所で観察できます。

void someMethod(){
    ClassInfo<B> b1 = (ClassInfo<B>) get1(); //does not compile
    ClassInfo<? extends B> b2 = (ClassInfo<? extends B>) get2(); //compiles
}

ClassInfo<A> get1() {
    return null;
}

ClassInfo<? extends A> get2() {
    return null;
}
于 2012-11-23T17:09:25.953 に答える
4

静的メソッドをオーバーライドすることはできません。したがって、同じ静的メソッドを宣言すると、新しいメソッドが作成されます。

public static ClassInfo<B> getClassInfo() { 
    return new ClassInfo<B>(B.class, A.getClassInfo());
}

ただし、戻り値の型を変更してメソッドを宣言すると、それは有効なメソッドの非表示もオーバーライドでもありません。したがって、 のgetClassInfo()メソッドclass AとのgetClassInfo()メソッドの両方がclass B競合します。クラスAのメソッドはクラスBにも見えるからです。

つまり、class Bから継承したメソッドと同じでclass A、戻り値の型が変更されています。また、メソッドの戻り値の型はメソッド シグネチャの一部とは見なされないためです。したがって、競合。

したがって、まったく同じ戻り値の型が必要です。その場合、クラス B は継承されたメソッドを無視し、独自のメソッドを使用します。

于 2012-11-23T17:04:17.453 に答える