抛出的Java抽象类

如果我有一个具有以下function的抽象类 –

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 col = new Collection(); B b = new B(); col.add(b); for(A a : col){ a.foo(); } 

我做了一些测试,似乎没有什么破坏,但我不明白为什么B的foo被调用而不是A的

因为Polymorphism

因为在运行时, Collection的实际对象类型是B所以调用了B.foo()

基本上,如果您将子类型对象分配给超类引用,则运行时多态性可确保调用实例方法的子类型版本,即当然它已被覆盖 。 如果没有,则呼叫将回退到超级版本。

什么是有效的方法覆盖?

重写方法必须具备

  • 相同的方法签名
  • 协变返回类型(可以返回子类型)
  • 不能抛出更广泛的检查exception (适用于你的问题和@ Dgrin91的评论,即仅仅因为被覆盖的方法带来了一些风险(抛出exception)并不意味着重写方法应该做同样的事情;因此,它可能不会抛出任何exception所有)
  • 不得使用限制较少的访问修饰符(可以保护公共但不保密)

这不是问题 – 实际上,通常的做法是在基类中抛出exception,其中未实现function,然后使用不抛出的东西覆盖实现。 覆盖方法后,不会调用基类的方法。

这样做的一个缺点是,如果用户恰好是检查(而不是“运行时”)类型,则需要捕获exception。 一个常见的解决方案是抛出未经检查的exception。

当然,如果抛出exception的唯一目的是指示未实现该function,则最好将相应的方法标记为abstract ,并让Java编译器捕获可能的违规。

正如另一张海报所指出的,这种行为是因为多态性而发生的。

您的集合被声明为A. B的元素,声明为扩展A,是-A A.这可以通过以下事实证实:您可以向集合中添加类型B的元素(期望A的实例)。

你的A.foo实现抛出一个exception,如果它被调用,它确实会抛出它。 另一方面,B覆盖方法foo,不抛出任何exception。 由于您添加到集合中的实例是B之一,因此被调用的是B.foo。 它不会改变你的for循环将实例声明为类型A(由于B是-A A,因此有效)。

您观察到的行为是预期的行为。

为了更好地理解,您可能想要创建:

 class C extends A {} 

并向集合中添加一个C实例。 迭代C的foo将委托给父类(A),然后按预期抛出exception。

您可以在不声明抛出的情况下覆盖方法。 这对于使用具体类的调用者很有用,例如,某些使用B类的人不需要try-catch,因为该方法的实现不会抛出任何东西。

Jon Skeet在此提供了更详细的解释: inheritance,方法签名,方法覆盖和throws子句