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

Java泛型方法的行为不符合预期,java,generics,Java,Generics,我有一个如下所示的方法,它给了我编译时错误 class Parent { public abstract <T extends Parent> T copy(); } 所以上面的方法解决了我的问题,但是如果我想使用父类copy方法,并且想在子类中做更多的事情,我必须在子类中键入CAST 所以考虑到这两个因素,你们有更好的解决问题的方法吗?你们需要手动将结果转换为T,比如 public <T extends Parent> T copy() {

我有一个如下所示的方法,它给了我编译时错误

class Parent {
  public abstract <T extends Parent> T copy();
}
所以上面的方法解决了我的问题,但是如果我想使用父类copy方法,并且想在子类中做更多的事情,我必须在子类中键入CAST


所以考虑到这两个因素,你们有更好的解决问题的方法吗?

你们需要手动将结果转换为T,比如

    public <T extends Parent> T copy() {

       return (T) new Child();
    }
public T copy(){
返回(T)新的子项();
}

泛型允许程序员指定
T
是什么类型。如果用户尝试以下操作

OtherChild child = <OtherChild>copy();
OtherChild=copy();
如果
OtherChild扩展了Parent
,但
OtherChild
不是
Child
,Java将期望
copy()
返回一种类型的
t
,或者在这种情况下返回
OtherChild
。由于
OtherChild
不是
Child
,因此会出现编译时错误。您可能希望按照Nikolay的建议,尝试将返回值强制转换为
T
,或者由最终用户决定,并将
copy()
的返回类型更改为
Child

编辑


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

问题在于,当您以这种方式声明方法时,泛型参数没有绑定到任何东西。编译器为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();
  }
}
抽象类父类{
公开摘要不可复制();
}
类子级扩展父级{
公共子副本(){
返回新的子对象();
}
}

你不需要泛型来完成你想做的事情

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);
    }
}
这一点在本手册中有专门介绍

编辑以响应OP添加到Q:


答案是您不调用
super.copy()
。您正在
copy()
方法中实例化一个新对象,这意味着涉及构造函数。子
的构造函数将调用父
的构造函数。在
Child
copy()。我已经编辑了上面的示例来说明我的意思。

你能发布一个独立的示例吗?你的意思是你想让我为父类和子类添加类吗?@ElliottFrisch现在明白了吗?请参阅已更改的问题。“给我编译时错误”:什么eror?如果我至少使用泛型,我不希望进行类型转换。@Mohdanan您想要的和语言允许的是两件不同的事情;)你可以试着看看,这篇帖子里的人也在讨论同样的问题problem@BrianRoach好的,那个么答案是Java不允许这样做吗?不,您必须强制转换。正如Eyal Schneider提到的,这是他的答案,这是个坏主意。这不是泛型的好用法。是的-我在你编辑的时候评论过+1这似乎很有帮助。如果这是唯一的解决方案,即使用类级别的泛型来实现方法级别的事情,那么我更喜欢类型转换:)似乎我在某个地方需要类型转换。在父复制方法中,我正在执行父p=this.getClass().newInstance();但我不能在父类中返回p。在这里,我们必须键入casting p,因为我们的父副本方法返回T。@MohdAdnan:如果您希望在父副本中有这样的默认行为,那么我看到两个选择:1)在构造函数中铸造2)传递类,并使用它创建实例。这不需要强制转换,但需要人工构造函数参数。无论如何,如果一个子类不遵守约定,并且为t指定了不正确的类型,那么这两种解决方案都将失败。请稍等,请看问题的第二部分。你会更好地理解我为什么需要泛型。我想避免在child中进行类型转换。嗯,你想让我写完整的代码:)想想我不创建对象的任何其他用例。但情况是一样的。顺便说一下,我正在使用反射在父类中创建对象。
abstract class Parent<T extends Parent<T>> {
  public abstract T copy();
}

class Child extends Parent<Child> {
  public Child copy() {
    return new Child();
  }
}
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);
    }
}