Java泛型方法的行为不符合预期

时间:2013-12-23 07:05:07

标签: java generics

我有一个给出编译时错误的方法。

class Parent {
  public abstract <T extends Parent> T copy();
}

和Child class

class Child extends parent{

public <T extends Parent> T copy() {

               return new Child();
        }

}

通过泛型我告诉类型橡皮擦我会返回子对象仍然不让我这样做。 有人对此有意见/解决方案吗?

我可以用下面给出的新方法解决我的问题。

class Parent {

public Parent copy(){
  //some stuff
}

}

class Child extends Parent{

   @Override
   public Child copy(){
         Child c = (Child)   super.copy();//I want to avoid this type casting here.

}
}

所以上面的方法解决了我的问题,但如果我想使用父类复制方法并且想在Child类中做更多的东西,我必须在子进程中使用TYPE CAST。

所以考虑到两者,你有更好的解决问题的方法吗?

4 个答案:

答案 0 :(得分:2)

问题在于,当您以这种方式声明方法时,您的泛型参数不会绑定到任何内容。编译器绑定T类型的唯一方法是在您调用方法的行中。例如,它将在以下调用中假定类型为Child:

Child  c = copy();

现在,如果您的代码被编译器认为是合法的,并且您执行了类似以下的操作,会发生什么?

GrandChild g = copy();

在这种情况下,您将在运行时获得意外的ClassCastException,这正是泛型试图避免的。

请注意,即使您像其他人建议的那样转换为T,也会收到编译器警告,因为风险仍然存在。

以下替代方案在类型安全方面更好,但有点麻烦:

abstract class Parent<T extends Parent<T>> {
  public abstract T copy();
}

class Child extends Parent<Child> {
  public Child copy() {
    return new Child();
  }
}

答案 1 :(得分:1)

你不需要Generics来做你想做的事。

Java允许协变返回类型:

abstract class Parent {
    protected int i; 

    public Parent(int i) {
        this.i = i;
    }
    public abstract Parent copy();
}

public class Child extends Parent {
    private int j;

    public Child(int i, int j) {
        super(i);
        this.j = j;
    }

    @Override
    public Child copy() {
        return new Child(this.i, this.j);
    }
}

Section 8.4.5 of the JLS

详细介绍了这一点

编辑以响应OP添加到Q:

答案是你不要打电话给super.copy()。您正在copy()方法中实例化一个新对象,这意味着构造函数已经涉及。 Child的构造函数将调用Parent的构造函数。在Child的{​​{1}}中,您只需调用copy()构造函数即可。我编辑了上面的例子来说明我的意思。

答案 2 :(得分:0)

泛型允许程序员指定T的类型。如果用户尝试类似

的内容
OtherChild child = <OtherChild>copy();

OtherChild extends ParentOtherChild不是Child,Java会期望copy()返回TOtherChild类型} 在这种情况下。由于OtherChild不是Child,因此会出现编译时错误。您可能希望尝试按照Nikolay的建议将返回值转换为T,或者将其保留给最终用户并将copy()的返回类型更改为Child

修改

如果要在问题中实现抽象方法,则需要使用强制转换方法;显然,在这里更改方法的返回类型是不可接受的。

答案 3 :(得分:-1)

您需要手动将结果投射到T,例如

    public <T extends Parent> T copy() {

       return (T) new Child();
    }