如果我有一个具有以下功能的抽象类 -
abstract class A{
void foo(String s) throws Exception{
throw new Exception("exception!");
}
}
然后是另一个扩展抽象类并实现自己的foo版本的类 -
class B extends A{
void foo(String s){
//do stuff that does *not* throw an exception
}
}
这会产生问题吗?具体在以下测试用例中 -
Collection<A> col = new Collection<A>();
B b = new B();
col.add(b);
for(A a : col){
a.foo();
}
我做了一些测试,似乎没有什么破坏,但我不明白为什么B的foo被调用而不是A的
答案 0 :(得分:11)
由于Polymorphism
。
因为在运行时Collection
中的实际对象类型是B
所以,B.foo()
被调用了。
基本上,如果您将子类型对象分配给超类引用,则运行时多态性可确保子类型的 < em>一个实例方法 被调用,即它当然是 重写 。如果没有,则呼叫将回退到超级版本。
重写方法必须
答案 1 :(得分:3)
这不是问题 - 实际上,在基类中抛出异常是常见的做法,其中未实现功能,然后使用不抛出的内容覆盖实现。覆盖方法后,不会调用基类的方法。
这样做的一个缺点是,如果用户恰好是检查(而不是“运行时”)类型,则需要捕获异常。一个常见的解决方案是抛出未经检查的异常。
当然,如果抛出异常的唯一目的是指示未实现该功能,则最好标记相应的方法abstract
,并让Java编译器捕获可能的违规。
答案 2 :(得分:0)
正如另一张海报所指出的,这种行为是因为多态性而发生的。
您的集合被声明为A. B的元素,声明为扩展A,是-A A.这可以通过以下事实证实:您可以向集合中添加类型B的元素(期望A的实例)
A.foo的实现会抛出异常,如果它被调用,确实会抛出异常。另一方面,B覆盖方法foo,而不是抛出任何异常。由于您添加到集合中的实例是B之一,因此被调用的是B.foo。它不会改变你的for循环将实例声明为类型A(由于B是-A A,因此有效)。
您观察到的行为是预期的行为。
为了更好地理解,您可能想要创建:
class C extends A {}
并向集合中添加一个C实例。迭代C的foo将委托给父类(A),然后按预期抛出异常。
答案 3 :(得分:0)
您可以在不声明抛出的情况下覆盖方法。这对于使用具体类的调用者很有用,例如某些使用B类的人不需要try-catch,因为该方法的实现不会抛出任何东西。
Jon Skeet在此提供了更详细的解释:Inheritance , method signature , method overriding and throws clause