我想覆盖子类中的泛型函数,如下所示:
超类:
public abstract class Metric {
public abstract <T extends Foo> T precompute(); // valid syntax
public abstract <T extends Foo> void distance(T arg); // valid syntax
public static class Foo {}
}
子类:
public class MetricDefault extends Metric {
@Override
public Bar precompute() { return new Bar(); } // Valid - return type extends Foo
@Override
public void distance(Bar arg) {} // Invalid ??? - argument type extends foo
public static class Bar extends Metric.Foo {}
}
泛型函数,当泛型类型是函数的返回值时,是有效的Java代码并且构建成功。
仅更改泛型类型的位置 - 使其成为函数的参数,而不是返回类型 - 并且它变为无效的Java代码。
为什么后一种情况无效?我该怎么做才能实现这个功能?
答案 0 :(得分:4)
方法声明
public abstract <T extends Foo> void distance(T arg);
表示该方法采用T
参数,调用者可以决定将Foo
的哪个子类型用作T
。
方法声明
public abstract <T extends Foo> T precompute();
表示该方法返回T
类型的某个对象,调用方可以决定将Foo
的哪个子类型用作T
。
最后一个方法无法真正实现(除了返回null
),因为你的方法并不真正知道这里要生成什么对象。
在你的具体类中,你试图覆盖这些方法 - 根据我的理解,你不应该允许它们,但是编译器可能比我更轻松。
我认为您真正想要的是像 Overbose 所发布的那样。
答案 1 :(得分:3)
public abstract class Metric<T extends Foo> {
public abstract T precompute(); // valid syntax
public abstract void distance(T arg); // valid syntax
public class Foo {}
}
然后,如果我理解Bar延伸Foo:
public class MetricDefault extends Metric<Bar> {
@Override
public Bar precompute() { return new Bar(); }
@Override
public void distance(Bar arg){}
public class Bar extends Foo {}
}
答案 2 :(得分:2)
因为超类中distance(T)
的签名指定允许具有对Metric
的引用的人传递任何扩展Foo
的类型作为参数。
第一种方法可以正常工作,因为任何引用Metric
的人都已准备好处理Bar
的返回类型,因为签名是指父类型Bar
。
答案 3 :(得分:1)
为此,您需要使T
成为该类型的参数,而不是方法。
您尝试这样做的方式,您的新方法不会覆盖抽象基本方法。执行类型擦除后,它等于以下代码:
public abstract class Metric {
public abstract Foo precompute(); // valid syntax
public abstract void distance(Foo arg); // valid syntax
public class Foo {}
}
public class MetricDefault extends Metric {
public Bar precompute() { return new Bar(); } // Valid - return type extends Foo
public void distance(Bar arg) {} // Signature not compatible - does not override distance(Foo)
public class Bar extends Foo {}
}
答案 4 :(得分:1)
正如@PaŭloEbermann所提到的,以下重写不应该是合法的
<T extends Foo>
T precompute();
Bar precompute()
JLSv3#8.4.8.3和#8.4.5中的规则禁止此覆盖。 Javac应该报告错误。
有两种可能的解释
规范省略了直观且明显的规则:“返回类型 - 可替代”关系应该是可传递的。 T->Foo->Bar
,Bar
覆盖T
是合法的。
javac是越野车。在验证覆盖时,它以某种方式错误Foo
作为方法#1的返回类型。
第一种解释非常难以置信。如果(经过定义的)关系是可传递的,那么检查它是否适用于2种任意类型就太难了。所以第二种解释可能更真实。