泛型返回类型的静态方法和继承

时间:2012-11-23 17:02:04

标签: java generics inheritance static-methods

返回类型的静态方法中的泛型似乎与继承相处得不好。请查看以下代码:

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());
    }
}

严格检查静态方法的原因是什么?我怎么能相处?更改方法名称似乎很尴尬。

2 个答案:

答案 0 :(得分:5)

B中的静态方法不会覆盖A中的静态方法,但会将其隐藏。 JLS 8.4.8.3明确表示返回类型必须是可替换的,否则将无法编译:

  

如果返回类型为R1的方法声明d1覆盖或隐藏了另一个返回类型为R2的方法d2的声明,那么对于d2,d1必须是return-type-substitutable(第8.4.5节),或者是编译时错误发生。

可替代性在JLS#8.4.5中定义:

  

当且仅当满足以下条件时,具有返回类型R1的方法声明d1是具有返回类型R2的另一个方法d2的return-type-substitutable:

     
      
  • [...]
  •   
  • 如果R1是参考类型,那么:   
        
    • R1是R2的子类型,或者R1可以通过未经检查的转换(第5.1.9节)转换为R2的子类型,或
    •   
    • R1 = | R2 |
    •   
  •   

在您的情况下:d1是B中的方法,R1是ClassInfo<B>,d2 A和R2中的方法是ClassInfo<A>ClassInfo<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;
}

答案 1 :(得分:4)

您无法覆盖静态方法。因此,当您声明相同的静态方法时,您将创建一个新方法。

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

但是,当您使用更改后的返回类型声明方法时,不是隐藏的有效方法,无法覆盖。因此,getClassInfo()的{​​{1}}方法和class A getClassInfo()方法相互冲突。因为A类的方法在B类中也是可见的。

因此,换句话说,class B具有相同的方法,因为它继承自class B,返回类型也有变化。并且因为返回类型的方法不被认为是方法签名的一部分。因此,冲突。

因此,您需要具有完全相同的返回类型。在这种情况下,B类将忽略继承的方法,并使用它自己的。