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